#include "stdafx.h"
#include "vp1_037.h"
#include "Screen.h"

#pragma warning(disable:4996)


CVP1_037_014::CVP1_037_014()
	: m_nClk(0)
	, m_pCMNB(nullptr)
	, m_bBusOut(false)
	, wire_RegNum(NO_REG)
	, m_nMemProcState(MS_WAIT_CAS)
	, m_bWait_WTD(false)
	, m_pMemory(nullptr)
	, m_bVSynced(false)
	, m_bDrawing(false)
	, m_nSincoLength(0)
	, m_nVideoStringCount(0)
	, m_nLineNum(0)
	, m_nLineByteNum(0)
	, m_pCombo(nullptr)
	, m_bRP1_in_prev(false)
	, m_bRP2_in_prev(false)
{
	m_nStartAddress = 0100000;
	reg.clear();
	m_IOPins.clear();
	m_pCombo = new uint8_t[1 << 12];
	ASSERT(m_pCombo);

	if (m_pCombo)
	{
		calculateCombo();
	}

	//принудительно выполним ресет, чтобы не усложнять внешними вызовами код.
	always_posedge_reset();
//---------------- тестовая часть ------------------------
	reg_RA = 0330; //экран в отладочных целях сразу сделаем как нужно.
	reg_M256 = 01000;
	m_pMemory = new uint16_t[65536];

	if (m_pMemory)
	{
		memset(m_pMemory, 0, 65536 * sizeof(uint16_t));
		FILE *f;

		//Загрузим в видеопамять какое-нибудь изображение
		if (f = fopen("!ROSE.PIC", "rb"))
		{
			fread(m_pMemory + 020000, 1, 040000, f);
			fclose(f);
		}

		//по-быстрому прочтём файл с тестами. Он обязательно должен быть.
	//	if (f = fopen("!bug_test2.raw", "rb"))
		if (f = fopen("791401.bin", "rb"))
	//  if (f = fopen("791404.bin", "rb"))
		{
			fread(m_pMemory, 1, 040000, f);
			fclose(f);
		}
		else
		{
			ASSERT(FALSE);
		}
	}

#if (OUTLOG)
	pLog = fopen("_out.log", "wt");
#endif
#if (VIDEOLOG_ON)
	pVideoLog = fopen("_videolog.log", "wt");
#endif
}

CVP1_037_014::~CVP1_037_014()
{
#if (OUTLOG)
	fclose(pLog);
#endif
#if (VIDEOLOG_ON)
	fclose(pVideoLog);
#endif

	if (m_pMemory)
	{
		delete[] m_pMemory;
	}

	if (m_pCombo)
	{
		delete[] m_pCombo;
	}
}

void CVP1_037_014::RegisterDevice(CCommonBus *pMPI)
{
	m_pCMNB = pMPI;
	ASSERT(m_pCMNB);
	//регистрируем выходные сигналы
	m_pCMNB->RegisterSignal(&m_IOPins.PIN_nAD,   CCommonBus::MPI_DATA_nAD);   //адрес-данные для ВП
	m_pCMNB->RegisterSignal(&m_IOPins.PIN_nRPLY, CCommonBus::MPI_LINE_nRPLY);
	m_pCMNB->RegisterSignal(&m_IOPins.PIN_nVIRQ, CCommonBus::MPI_LINE_nIRQ0);
	m_pCMNB->RegisterSignal(&m_IOPins.PIN_nIRQ2, CCommonBus::MPI_LINE_nIRQ2);
	m_pCMNB->RegisterSignal(&m_IOPins.PIN_nIAKO, CCommonBus::MPI_LINE_nIAK1);
}

//рассчитаем заранее таблицу сканкодов и векторов прерывания
//здесь быстрота не важна, здесь важна наглядность
void CVP1_037_014::calculateCombo()
{
	bool C[8];
	bool Y[8];
	bool X[10];

	for (int regRus = 0; regRus < 2; ++regRus)
	{
		for (int Caps_in = 0; Caps_in < 2; ++Caps_in)
		{
			for (int AR2_in = 0; AR2_in < 2; ++AR2_in)
			{
				for (int Ctrl_in = 0; Ctrl_in < 2; ++Ctrl_in)
				{
					for (int Shift_in = 0; Shift_in < 2; ++Shift_in)
					{
						for (int PIN_X = 0; PIN_X < 15; ++PIN_X) //действительны только 0..9
						{
							for (bool &i : X)
							{
								i = false;
							}

							if (PIN_X < 10)
							{
								X[PIN_X] = true;
							}

							for (int PIN_Y = 0; PIN_Y < 8; ++PIN_Y)
							{
								for (bool &i : Y)
								{
									i = false;
								}

								if (PIN_Y < 8)
								{
									Y[PIN_Y] = true;
								}

								uint32_t dwX;
								bita2data(X, dwX, 10);
								bool X9_6 = ((dwX & 0x3c0) != 0); //X[9:6]
								bool X5_4 = ((dwX & 0x30) != 0);  //X[5:4]
								bool X3_0 = ((dwX & 0xf) != 0);   //X[3:0]
								C[0] = !((Y[6] || ((Y[4] || (!Y[3] && (!Y[1] || Y[2]))) && !Y[5])) && !Y[7]);
								C[1] = !((Y[5] || Y[4] || (!Y[2] && !Y[3])) && !(Y[7] || Y[6]));
								C[2] = !(!(Y[7] || Y[6] || Y[5] || Y[4]));
								C[3] = !((X[8] || ((X[6] || ((X[4] || !(X[3] || X[1])) && !X[5])) && !X[7])) && !X[9]);
								bool NET00047 = (X[5] && (Y[7] || Y[6] || Y[5] || Y[4]));
								C[4] = !(!(X[2] || X[3]) && ((X5_4 && !(!Shift_in ^ NET00047)) || (!(X[9] || X[8]) && (X[7] || X[6] || !(X[5] || X[4])))));
								C[5] = !(((!(!Caps_in ^ regRus) || Ctrl_in) && !X5_4) || !(!Shift_in || !X9_6) || X3_0);
								C[6] = !(!X9_6 || Ctrl_in);
								C[7] = AR2_in || (((Y[1] && X[1]) || (X[2] && Y[1]) || (Y[3] && X[1]) || X[0]) && !(X[0] && Y[3]));
								//index
								//11 10 09 08 07 06 05 04 03 02 01 00
								//Rr Ca Ar Ct Sh [  Y   ] [    X    ]
								int index = (PIN_X & 0xf) | (PIN_Y & 7) << 4;

								if (Shift_in)
								{
									index |= 1 << 7;
								}

								if (Ctrl_in)
								{
									index |= 1 << 8;
								}

								if (AR2_in)
								{
									index |= 1 << 9;
								}

								if (Caps_in)
								{
									index |= 1 << 10;
								}

								if (regRus)
								{
									index |= 1 << 11;
								}

								//value
								//бит 7 - адрес вектора: 0 - 060, 1 - 0274
								//биты 6..0 - сканкод
								uint8_t nC;
								bita2data(C, nC, 8);
								m_pCombo[index] = nC;
							}
						}
					}
				}
			}
		}
	}
}



/* Выполнение модели по переднему фронту ТЧ */
void CVP1_037_014::eval_p(int nClk)
{
	m_nClk = nClk;
	snap_reg = reg; //делаем снапшот регистров. в левой части выражений используем reg, в правой - snap_reg
	assign_in();
	always_a();
	//выполняем все always для переднего фронта
	always_all(); //alwaysы с чувствительностью (*)
	always_posedge_clk();
	assign_func();
	assign_out();   //вычисляем все значения на выводах микросхемы
#if (OUTLOG)
	OutLog(m_nClk, "/");
#endif
}

/* Выполнение модели по заднему фронту ТЧ */
void CVP1_037_014::eval_n(int nClk)
{
	m_nClk = nClk;
	snap_reg = reg; //делаем снапшот регистров. в левой части выражений используем reg, в правой - snap_reg
	assign_in();
	always_a();
	//выполняем все always для заднего фронта
	always_all(); //alwaysы с чувствительностью (*)
	always_negedge_clk();
	assign_func();
	assign_out();   //вычисляем все значения на выводах микросхемы
#if (OUTLOG)
	OutLog(m_nClk, "\\");
#endif
}

//упрощенный вариант
void CVP1_037_014::eval(int nClk)
{
	m_nClk = nClk;
	snap_reg = reg; //делаем снапшот регистров. в левой части выражений используем reg, в правой - snap_reg
	assign_in();
	always_a();
	always_all(); //alwaysы с чувствительностью (*)
	always_posedge_clk();
	always_negedge_clk();
	assign_func();
	assign_out();   //вычисляем все значения на выводах микросхемы
#if (OUTLOG)
	OutLog(m_nClk, "^");
#endif
}

void CVP1_037_014::assign_in()
{
	//______________________________________________________________________________
	// Input Pins
	//
	wire_sync_in_prev = wire_sync_in;
	wire_sync_in      = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nSYNC);
	wire_sync_in_rise = !wire_sync_in_prev &&  wire_sync_in;
	wire_sync_in_fall =  wire_sync_in_prev && !wire_sync_in;
	wire_din_in_prev  = wire_din_in;
	wire_din_in       = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nDIN);
	wire_din_in_rise  = !wire_din_in_prev &&  wire_din_in;
	wire_din_in_fall  =  wire_din_in_prev && !wire_din_in;
	wire_dout_in_prev = wire_dout_in;
	wire_dout_in      = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nDOUT);
	wire_dout_in_rise = !wire_dout_in_prev &&  wire_dout_in;
	wire_dout_in_fall =  wire_dout_in_prev && !wire_dout_in;
	wire_bus_io       =  wire_dout_in || wire_din_in;
	wire_iaki_in      = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nIAK0); //если iaki_in == 0, то выполнять функцию eval для мпи смысла нет.
	wire_init_in      = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nINIT);

	if (wire_sync_in && wire_din_in_rise)
	{
		wire_RegRD = wire_RegNum;
	}
	else if (wire_din_in_fall || wire_sync_in_fall)
	{
		wire_RegRD = NO_REG;
		wire_rply_reg = false;
	}

	if (wire_sync_in && wire_dout_in_rise)
	{
		wire_RegWR = wire_RegNum;
	}
	else if (wire_dout_in_fall || wire_sync_in_fall)
	{
		wire_RegWR = NO_REG;
		wire_rply_reg = false;
	}

	assign_in_kbd();
}

void CVP1_037_014::assign_in_kbd()
{
	//input pins уже принято ранее, но если eval_kbd будет вызываться асинхронно, только при событиях клавиатуры, то
	// надо будет что-то придумать
	//wire_init_in = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nINIT);
	//получаем данные по линии Y, на входе число 0..7
	//0 - означает, что ни одна линия не задействована, т.е. задействована виртуальная линия 0
	wire_Y = m_IOPins.PIN_Y & 0x7;
	//получаем данные по линии X, на входе число 0..9 или -1
	//-1 (15) означает, что ни одна линия не задействована, т.е. ни одна клавиша не нажата.
	wire_X = m_IOPins.PIN_X & 0xf;
}

void CVP1_037_014::always_a()
{
	if (wire_sync_in_rise)
	{
		register auto wire_ad_in = (uint16_t)~m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD);
		reg_A = wire_ad_in;

		switch (wire_ad_in & 0177776)
		{
			case 0177660:
				wire_RegNum = CSR_REG;
				break;

			case 0177662:
				wire_RegNum = DAT_REG;
				break;

			case 0177664:
				wire_RegNum = SCR_REG;
				break;

			case 0177716:
				wire_RegNum = SEL1_REG;
				break;

			default:
				wire_RegNum = NO_REG;
		}
	}
}

void CVP1_037_014::always_all()
{
	//ВП1-037
	if (wire_dout_in_rise)
	{
		reg_wtbt_rc = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nWTBT);
	}

	if (wire_RegWR == SCR_REG)
	{
		register auto ad = (uint16_t)~m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD);
		reg_RA   = ad & 0377;
		reg_M256 = ad & 01000;
		wire_rply_reg = true;
	}

	//ВП1-014
	wire_irq_ena = reg_IEN && snap_reg.READY && reg_irq_fc; //признак выдачи вектора внешнему устройству, если разрешено

	if (wire_din_in_rise)
	{
		reg_irq_rc = wire_irq_ena;
	}

	if (wire_IACK)
	{
		reg_irq_fc = false;
	}
	else if (!snap_reg.READY)
	{
		reg_irq_fc = true;
	}

	if (wire_init_in)
	{
		reg_IEN = false; //false - прерывания запрещены, true - разрешены
	}
	else
	{
		if (wire_RegWR == CSR_REG) //posedge CSRWR
		{
			register auto ad = (uint16_t)m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD);
			reg_IEN = !!(ad & (1 << 6)); //признак разрешения прерывания от клавиатуры
			wire_rply_reg = true;
		}
	}

	always_all_kbd();
}

void CVP1_037_014::always_all_kbd()
{
	register bool XANY = (wire_X < 10); //X[9:0]

	if (snap_reg.PRESS && XANY)
	{
		register int index = wire_X | (snap_reg.Y_in << 4);

		if (m_IOPins.PIN_SHIFT)
		{
			index |= 1 << 7;
		}

		if (m_IOPins.PIN_CTRL)
		{
			index |= 1 << 8;
		}

		if (m_IOPins.PIN_EC2) //АР2
		{
			index |= 1 << 9;
		}

		if (m_IOPins.PIN_EC1) //Caps
		{
			index |= 1 << 10;
		}

		if (reg_regRus)
		{
			index |= 1 << 11;
		}

		register uint8_t nC = m_pCombo[index];
		reg_vect_rc = nC & 0200;
		reg_data = nC & 0177;
	}

	if (wire_init_in)
	{
		reg.Y_in = 0;
		reg_regRus = false;
		reg.BUSY = true;
		reg.ready_fc = true;
		reg.READY = false;
	}
	else
	{
		// это - триггер переключения РУС/ЛАТ, если 1 - Рус, если 0 - Лат
		// в оригинале - не работающий из-за неправильного условия инициализации
		// поэтому, чтобы он и дальше не работал - этот if надо просто закомментировать
		if (wire_X == 1)
		{
			if (snap_reg.Y_in == 7)
			{
				reg_regRus = false;
			}
			else if (snap_reg.Y_in == 6)
			{
				reg_regRus = true;
			}
		}

		if (snap_reg.PRESS || XANY)
		{
			reg.Y_in = wire_Y;
		}

		register bool b0 = (snap_reg.release_fc || snap_reg.PRESS);

		if (snap_reg.READY)
		{
			reg.BUSY = true;
		}
		else if (!b0) //RELEASE
		{
			reg.BUSY = false;
			reg.release_fc = true;
		}

		if (!snap_reg.BUSY && b0)
		{
			reg.READY = true;
			reg.ready_fc = true;
		}
		else
		{
			if (wire_RegRD == DAT_REG)
			{
				reg.ready_fc = false; //сброс готовности при чтении из порта данных
			}
			else
			{
				if (!snap_reg.ready_fc)
				{
					reg.READY = false;
				}
			}
		}
	}

	if (!m_bRP2_in_prev && m_IOPins.PIN_RP2) //posedge RP1_in or posedge RP2_in
	{
		reg.PRESS = true;
		reg.release_fc = false;
	}
	else if (!m_bRP1_in_prev && m_IOPins.PIN_RP1)
	{
		reg.PRESS = m_IOPins.PIN_RP2; //PRESS <- false
	}

	m_bRP2_in_prev = m_IOPins.PIN_RP2;
	m_bRP1_in_prev = m_IOPins.PIN_RP1;
}


void CVP1_037_014::always_posedge_clk()
{
}

void CVP1_037_014::always_negedge_clk()
{
	if (reg_RASEL)
	{
		reg_TRPLY = true;
	}
	else if (!wire_bus_io)
	{
		reg_TRPLY = false;
		m_IOPins.PIN_WTD = false;
	}

	switch (reg_PC) //фаза работы ВП1-037
	{
		case 0: //пустая фаза - ничего не делаем
			break;

		case 1: //видео цикл - фаза установки CAS
			if (nVGateCounter + nHGateCounter == 0)
			{
				m_IOPins.PIN_CAS = (1 << 1 | 1 << 0); //оба cas0 и cas1 (оба байта)
			}

			break;

		case 2:  //видео цикл - фаза установки WTI
			if (nVGateCounter + nHGateCounter == 0)
			{
				m_IOPins.PIN_WTI = (reg_M256 || (reg_LC >= 0300)); //(если обычный режим, или расширенный режим) и видеоцикл
			}

			break;

		case 3: //видео цикл - конец видеоцикла
			m_IOPins.PIN_WTI = false; //конец видеоцикла
			m_IOPins.PIN_CAS = 0;

			if ((reg_VA & 016) == 0) //когда reg_PC == 3 и 7
			{
				//если адрес слова reg_VA == 0, 020, 040, 060 в строке
				reg_VTSYN = true;
			}

			break;

		case 4: //фаза обработки доступа к памяти - проверка адреса
			//если адрес из разрешённого диапазона 0..077777 и происходит обмен на шине, то true.
			reg_RASEL = !(reg_A & 0100000) && wire_sync_in && wire_bus_io;

			if (reg_RASEL)
			{
				m_IOPins.PIN_WE = wire_dout_in; //если адрес тот, и запись в память, то будем записывать
			}

			break;

		case 5: //фаза обработки доступа к памяти - установка CAS, если адрес правильный
			if (reg_RASEL) //если запрос на обмен с памятью и pc==6 или 7
			{
				if (wire_dout_in && reg_wtbt_rc) //если байтовая операция и цикл доступа к памяти (и запись, при чтении reg_wtbt_rc не имеет значения)
				{
					if (reg_A & 1) //если адрес нечётный
					{
						m_IOPins.PIN_CAS = (1 << 1); //только cas1 (ст. байт)
					}
					else //если чётный
					{
						m_IOPins.PIN_CAS = (1 << 0); //только cas0 (мл. байт)
					}
				}
				else //если словная операция
				{
					m_IOPins.PIN_CAS = (1 << 1 | 1 << 0); //оба cas0 и cas1 (оба байта)
				}
			}

			break;

		case 6: //фаза обработки доступа к памяти - всё, адрес микросхемам отдали, теперь снова видеоадрес
			reg_RASEL = false; //всё, адрес больше не выбираем, теперь видеоадрес
			break;

		case 7: //завершение цикла доступа к памяти по записи, и счётчики видеоадреса
		{
			m_IOPins.PIN_WE = false; //прекращаем запись
			m_IOPins.PIN_WTD = reg_TRPLY && wire_din_in; //а чтение может быть только начинаем
			m_IOPins.PIN_CAS = 0; //после того как CAS пропадает, начинается цикл чтения у микросхем памяти

			if ((reg_VA & 016) == 0) //когда reg_PC == 3 и 7
			{
				//если reg_VA == 0, 020, 040, 060
				reg_VTSYN = true;
			}
			else if ((reg_VA & 036) == 010)
			{
				//если reg_VA == 010, 050
				reg_VTSYN = false;
			}

			//
			// Word counter (within line)
			//
			// reg_VA - видео адрес в пределах области экрана 0..37777 в байтах
			// инкрементируется всегда на 2 и указывает на слово.
			// младшие 6 бит - адрес слова в строке, остальные - адрес строки
			register uint16_t dww = (reg_VA & 076) + 2; //переходим к следующему слову
			register uint16_t dwa = reg_VA & 037700;

			if (nHGateCounter > 0) //считаем служебное поле в строке
			{
				if (--nHGateCounter == 0) //отсчитали 16. служебных слов
				{
					dww = 0;
				}
			}
			else if (dww == 0100)
			{
				//если отсчитали 32. слова, нужно теперь считать служебные
				dww = 0;
				nHGateCounter = 16;
				//
				// Line counter
				//
				register uint16_t dwl = (reg_LC - 1) & 0377;

				if (nVGateCounter > 0) //считаем служебные
				{
					if (--nVGateCounter == 0) //отсчитали 64 служебные строки
					{
						dwl = 0377;  //снова начнём считать информационные
					}

					//если считаем служебные строки, выбираем из них 353..350
					if ((reg_LC & 074) == 050) //if (050 <= nVGateCounter && nVGateCounter <= 053)
					{
						//если строки 351, 350
						if (!(reg_LC & (1 << 1))) //if (050 == nVGateCounter || nVGateCounter == 051) 
						{
							//применяем новое смещение
							dwa = reg_RA << 6; //VA[13:6] <= RA[7:0];
						}

						//при этом на новую строку не переходим. в одной крутимся.
					}
					else
					{
						//переход на следующую строку
						dwa += 0100; //VA[13:6] <= VA[13:6] + 8'b00000001;
					}
				}
				else
				{
					if (dwl == 0377)
					{
						//если отсчитали 256 информационных строк, нужно теперь считать служебные.
						nVGateCounter = 64;
					}

					//переход на следующую строку
					dwa += 0100; //VA[13:6] <= VA[13:6] + 8'b00000001;
				}

				reg_LC = dwl;
			}

			reg_VA = (dwa | dww) & 037776;
		}
		break;
	}

	reg_PC = (++reg_PC) & 7;
}

void CVP1_037_014::assign_func()
{
	//______________________________________________________________________________
	//
	if (wire_din_in && wire_iaki_in)
	{
		wire_IACK     =  reg_irq_rc; //признак запроса внешним устройством вектора
		wire_iako_out = !reg_irq_rc;
	}
	else
	{
		wire_IACK     = false; //признак запроса внешним устройством вектора
		wire_iako_out = false;
	}
}

void CVP1_037_014::assign_out()
{
	//______________________________________________________________________________
	// Output Pins
	//
	if (wire_IACK)
	{
		if (!m_bBusOut)
		{
			m_bBusOut = true;
			m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, reg_vect_rc ? ~0274 : ~060);
		}
	}
	else if (wire_RegRD != NO_REG)
	{
		if (!m_bBusOut)
		{
			m_bBusOut = true;

			switch (wire_RegRD)
			{
				case CSR_REG: //если читаем из 177660
				{
					register uint16_t nAD = 0;

					if (!reg_IEN) //если прерывания запрещены
					{
						nAD |= 0100; //то бит 6
					}

					if (reg.READY) //если сканкод готов
					{
						nAD |= 0200; //то бит 7
					}

					m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~nAD);
					wire_rply_reg = true;
				}
				break;

				case DAT_REG: //если читаем из 177662
					m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~reg_data);
					wire_rply_reg = true;
					break;

				case SCR_REG: //если читаем из 177664
				{
					register uint16_t nAD = (reg_RA & 0377) | reg_M256;
					m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~nAD);
					wire_rply_reg = true;
				}
				break;

				case SEL1_REG: // start address vector 177716
					m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~0200); // ~m_nStartAddress);
					break;
			}
		}
	}
	else if (m_bBusOut)
	{
		m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, -1); //снимать данные с шины только тогда, когда их выдавали. Для повышения быстродействия
		m_bBusOut = false;
	}

	register bool wire_rply_out = reg_TRPLY && !reg_RASEL;
	m_pCMNB->SetSignalOE(&m_IOPins.PIN_nRPLY, (wire_rply_out || wire_rply_reg || wire_IACK));
	m_pCMNB->SetSignalOE(&m_IOPins.PIN_nIAKO, wire_iako_out); //выход, передача векторного прерывания дальше по цепочке
	m_pCMNB->SetSignalOE(&m_IOPins.PIN_nVIRQ, wire_irq_ena);
	m_IOPins.PIN_A  = ((reg_RASEL ? reg_A : (reg_VA | 040000)) >> 1) & 037777;
	m_IOPins.PIN_nE = !((wire_din_in && wire_sync_in) && (reg_A < 0177600));
	//!(если считаем служебные строки и выбираем из них 353..350)
	register bool SYNC2 = !nVGateCounter || ((reg_LC & 074) != 050) || ((reg_LC & 3) == 3);
	register bool SYNC5 = reg_VTSYN || (SYNC2 && !nHGateCounter);
	m_IOPins.PIN_SINCO = (SYNC2 ^ SYNC5);
}

void CVP1_037_014::always_posedge_reset()
{
	//______________________________________________________________________________
	//
	// Video synchronization
	//
	reg_VTSYN = true;
	reg_PC = 7;
	nHGateCounter = 0;
	nVGateCounter = 64;
	//
	// Word counter (within line)
	//
	reg_VA &= 037700; //VA[5:1] <= 5'b00000;
	//
	// Line counter
	//
	reg_LC = 0377;
}


/*
Вызывается каждый такт 6МГц
*/
void CVP1_037_014::memPostProc(CScreen *pScr)
{
	/*сформируем адрес.
	*сперва приходит ras, затем cas.
	*если cas не приходит, то это режим регенерации
	*если сперва приходит cas, то это страничная регенерация
	*пока ras и cas оба 0 приходит сигнал W/R чтобы определить, тип операции
	*сигнал W/R устанавливается между ras и cas,
	*если запись, то снимается быстро, вместе с ним ставятся и снимаются данные
	*если чтение, то снимается после съёма cas, данные выставляются после установки cas, и снимаются после съёма cas
	*WTD выставляется сразу после снятия WE чтобы не конфликтовали операции. сперва делается запись, а потом чтение по
	*этому же адресу.
	*/
	if (m_bWait_WTD) //если выставляли на шину данные
	{
		if (!m_IOPins.PIN_WTD) //если дождались сигнала, когда данные можно уже снять
		{
			m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~0); //снимаем
			m_bWait_WTD = false;
		}
	}

	switch (m_nMemProcState)
	{
		case MS_WAIT_CAS: //Ожидаем прихода CAS
			if (m_nMemProcOperation = m_IOPins.PIN_CAS) //если CAS пришёл, определим тип операции
			{
				m_nMemProcAddress = m_IOPins.PIN_A; //примем адрес

				//m_nMemProcAddress - индекс в словном массиве, для байтового массива, нужно умножить на 2
				if (m_IOPins.PIN_WE) //если операция записи в память
				{
					switch (m_nMemProcOperation)
					{
						//case 0: //нет операции,сюда никак не попадает
						//  break;
						case 1: //младший байт
						{
							register auto *pByteAddr = reinterpret_cast<uint8_t *>(&m_pMemory[m_nMemProcAddress]);
							*pByteAddr = ~LOBYTE(m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD));
							break;
						}

						case 2: //старший байт
						{
							register auto *pByteAddr = reinterpret_cast<uint8_t *>(&m_pMemory[m_nMemProcAddress]) + 1;
							*pByteAddr = ~HIBYTE(m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD));
							break;
						}

						case 3: //слово
							m_pMemory[m_nMemProcAddress] = ~LOWORD(m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD));
							break;
					}

					m_nMemProcState = MS_WAIT_WRITE_CAS_CANCEL; //пойдём ждать снятия CAS
				}
				else
				{
					//если не запись, то это может быть чтение или цикл экрана
					m_nMemProcState = MS_OPERATE; //пойдём выяснять
				}
			}

			break;

		case MS_OPERATE:
			if (m_IOPins.PIN_WTI)
			{
				//Вот тут надо брать слово m_Memory[m_nMemProcAddress] и выводить его на экран
				//по текущим координатам луча.
				if (m_bDrawing)
				{
					register uint16_t data = m_pMemory[m_nMemProcAddress];
					pScr->SetExtendedMode(!reg_M256);
					pScr->PrepareScreenLineWordRGB32(m_nLineNum, m_nLineByteNum, data); m_nLineByteNum += 2;
					//!!!тут такой дефект: точный алгоритм не совместим с текущими функциями и эмуляцией затухания люминофора.
					//!!! потому что не понятно, как в режиме РП, когда не выдаётся WTI использовать эту функцию, подавая на входе 0
					//!!! чтобы ничего не рисовалось, но как бы ход луча шёл.
					//pScr->DrawScreen(FALSE); //для отладки
#if (VIDEOLOG_ON)
					fprintf(pVideoLog, "*"); fflush(pVideoLog);
#endif
				}

#if (VIDEOLOG_ON)
				else
				{
					fprintf(pVideoLog, "-"); fflush(pVideoLog);
				}

				fprintf(pVideoLog, "%06o ", m_nMemProcAddress * 2); fflush(pVideoLog);
#endif
				m_nMemProcState = MS_WAIT_CAS_CANCEL; //пойдём ждать снятия CAS
				break; //выходим
			}

		//если не WTI, переходим к следующему case, проверим не пропал ли cas и не операция ли чтения
		case MS_WAIT_WRITE_CAS_CANCEL:
			if (m_IOPins.PIN_CAS == 0) //если CAS пропал
			{
				if (m_IOPins.PIN_WTD) //выдача данных на шину
				{
					/*причём PIN_WTD устанавливается после сброса CAS, или за пол-такта до снятия,
					зависит от того, в каком режиме работаем в потактовом или полутактовом
					поэтому, при снятии CAS, WTD уже гарантированно будет выставлен
					*/
					switch (m_nMemProcOperation)
					{
						//case 0: //нет операции,сюда никак не попадает
						//  break;
						case 1: //младший байт
							m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~(m_pMemory[m_nMemProcAddress] & 0377));
							break;

						case 2: //старший байт
							m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~(m_pMemory[m_nMemProcAddress] & 0177400));
							break;

						case 3: //слово
							m_pCMNB->SetSignal(&m_IOPins.PIN_nAD, ~m_pMemory[m_nMemProcAddress]);
							break;
					}

					m_bWait_WTD = true; //обозначим, что надо дождаться сигнала снятия данных с шины
				}

				m_nMemProcState = MS_WAIT_CAS; //и идём снова ожидать появления CAS
			}

			break;

		case MS_WAIT_CAS_CANCEL:
			if (m_IOPins.PIN_CAS == 0) //CAS пропал
			{
				m_nMemProcState = MS_WAIT_CAS; //идём снова ожидать появления CAS
			}

			break;
	}
}


/*
Вызывается каждый такт 6МГц
*/
void CVP1_037_014::videoProc(CScreen *pScr)
{
	/*
	По сигналу WTI в регистр сдвига помещается новое слово
	Сигнал SINCO (PIN_SINCO) - синхросигнал.
	надо ловить длительность сигнала.
	28 тактов 6Мгц - конец строки и перевод луча на новую строку
	100 тактов - обратный ход луча? их идёт 8 штук подряд, как только встретим первый импульс - надо перерисовать экран.
	60 тактов - сразу за 100 тактными. Будем считать, что это признак начала рисования на экране.

	Выяснено, что в кадре получается 318. строк, информация начинается с 41. строки (отсчёт с 0), заканчивается на 296. строке,
	с 297. строки - остаток кадра.
	В режиме расширенной памяти, информация начинается с 41. строки (отсчёт с 0), заканчивается на 104. строке,
	с 105. строки - остаток кадра, импульсы WTI не подаются, но строки отсчитываются как обычно.
	*/
#if (VIDEOLOG_ON)
	static int nCadreCount = 0;
	static bool nIRQ2_Prev = true;
	bool bIrq2 = m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nIRQ2);

	if (!nIRQ2_Prev && bIrq2)
	{
		fprintf(pVideoLog, "\nClock: %08d - IRQ2 _/ ", m_nClk); fflush(pVideoLog);
	}
	else if (nIRQ2_Prev && !bIrq2)
	{
		fprintf(pVideoLog, "\nClock: %08d - IRQ2 \\_ ", m_nClk); fflush(pVideoLog);
	}

	nIRQ2_Prev = bIrq2;
#endif

	if (m_IOPins.PIN_SINCO) //пока SINCO == 1
	{
		m_nSincoLength++; //считаем
	}
	else if (m_nSincoLength) //стало 0 - перестаём считать
	{
		//и смотрим, что получилось
		if (m_nSincoLength == 60)
		{
			m_bVSynced = true;
			//установка координат луча в начало экрана
			m_nVideoStringCount = 0;
#if (VIDEOLOG_ON)
			fprintf(pVideoLog, "%08d: %03d ", m_nClk, m_nVideoStringCount); fflush(pVideoLog);
#endif
		}
		else if (m_bVSynced)
		{
			if (m_nSincoLength == 100)
			{
				//прорисовка экрана
				m_bVSynced = false;
				pScr->DrawScreen(true);
#if (VIDEOLOG_ON)
				fprintf(pVideoLog, "\n\n\n\nclock %d, kadr: %d\n", m_nClk, ++nCadreCount); fflush(pVideoLog);
#endif
			}
			else if (m_nSincoLength == 28)
			{
				//переход на новую строку.
				if (++m_nVideoStringCount == 41)
				{
					m_bDrawing = true;
					m_nLineNum = -1;
				}

				if (m_bDrawing)
				{
					if (++m_nLineNum >= 256)
					{
						m_bDrawing = false;
					}

					m_nLineByteNum = 0;
				}

#if (VIDEOLOG_ON)
				fprintf(pVideoLog, "\n%08d: %03d ", m_nClk, m_nVideoStringCount); fflush(pVideoLog);
#endif
			}
		}

		m_nSincoLength = 0;
	}
}




///////////////////////////////////////////////////////////////////////////////////////////////

CVP1_037_014_BK11::CVP1_037_014_BK11()
	: CVP1_037_014()
	, wire_D28C_A_prev(false)
	, m_bIOPIN_SINCO_prev(false)
	, reg_D28Q_A(false)
{
	m_nStartAddress = 0140000;
	//тестовая часть
	m_IOPinsBK11.REG_177716Mem = 016000;
	m_IOPinsBK11.reg_177662W = 002400;
}

CVP1_037_014_BK11::~CVP1_037_014_BK11()
{}


void CVP1_037_014_BK11::assign_out()
{
	CVP1_037_014::assign_out();

	if (reg_RASEL)
	{
		//если адрес операции с ОЗУ
		switch (reg_Window)
		{
			case 040000: //если окно 0
				m_IOPins.PIN_A &= 017777;
				m_IOPins.PIN_A |= (m_IOPinsBK11.REG_177716Mem & (1 << 14 | 1 << 13 | 1 << 12)) << 1;
				break;

			case 0100000: //если окно 1
				m_IOPins.PIN_A &= 017777;
				m_IOPins.PIN_A |= (m_IOPinsBK11.REG_177716Mem & (1 << 10 | 1 << 9 | 1 << 8)) << 5;
				break;

			default: //если ни одно окно
				m_IOPins.PIN_A |= 0140000;
		}
	}
	else
	{
		//если видео адрес
		if (m_IOPinsBK11.reg_177662W & 0100000) //если отображаем экран 1, стр.6
		{
			m_IOPins.PIN_A |= 0140000;
		}
	}
}


void CVP1_037_014_BK11::always_posedge_clk()
{
	CVP1_037_014::always_posedge_clk();

	if (!(m_IOPinsBK11.reg_177662W & 040000)) //если прерывание от таймера 50Гц разрешено
	{
		//D3.2 К555ТМ2
		if (m_bIOPIN_SINCO_prev && !m_IOPins.PIN_SINCO)
		{
			m_pCMNB->SetSignal(&m_IOPins.PIN_nIRQ2, reg_D28Q_A); //генерируем прерывание
		}
	}

	m_bIOPIN_SINCO_prev = m_IOPins.PIN_SINCO;
	wire_D28C_A = m_IOPins.PIN_SINCO && !reg_D28Q_A;

	//К555ИЕ5.1
	if (m_IOPins.PIN_WTI)
	{
		reg_D28Q_A = false;
	}
	else if (wire_D28C_A_prev && !wire_D28C_A)
	{
		reg_D28Q_A = !reg_D28Q_A;
	}

	wire_D28C_A_prev = wire_D28C_A;
}

void CVP1_037_014_BK11::always_a()
{
	if (wire_sync_in_rise)
	{
		register auto ad = (uint16_t)~m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD);
		//D27 К555ТМ7
		reg_Window = ad & 0140000; //040000 - адрес окна 0, 0100000 - адрес окна 1, остальные - не имеют значения
		register bool A15_vp037 = (reg_Window == 0100000) && !(m_IOPinsBK11.REG_177716Mem & 033); //если выбрано окно 1 и не выбрано хоть какое-то ПЗУ

		if (A15_vp037)
		{
			ad &= 077777; //то бит 15 равен 0, чтобы RPLY выдавался на доступ к ОЗУ
		}

		reg_A = ad;

		switch (ad & 0177776)
		{
			case 0177660:
				wire_RegNum = CSR_REG;
				break;

			case 0177662:
				wire_RegNum = DAT_REG;
				break;

			case 0177664:
				wire_RegNum = SCR_REG;
				break;

			case 0177716:
				wire_RegNum = SEL1_REG;
				break;

			default:
				wire_RegNum = NO_REG;
		}
	}
}

void CVP1_037_014_BK11::always_all()
{
	CVP1_037_014::always_all();

	if (!m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nACLO))
	{
		m_IOPinsBK11.reg_177662W = 0;
	}
	else if (wire_RegWR == DAT_REG)
	{
		register auto ad = (uint16_t)~m_pCMNB->GetSignal(CCommonBus::MPI_DATA_nAD);
		m_IOPinsBK11.reg_177662W = ad & 0177400;
		wire_rply_reg = true;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////
#if (OUTLOG)
void CVP1_037_014::OutLog(int nClk, const char *str)
{
	static int nCount = 0;

	if (!((nCount++) % 20))
	{
		fprintf(pLog, "    clk   \\PC\\VTSYN\\CAS322/RASEL*TRPLY\\HGATE\\VA  \\VGATE\\LC");
		fprintf(pLog, "    CAS RAS WE WTD WTI SINCO\n");
	}

	fprintf(pLog, "%s %08d", str, nClk);
	fprintf(pLog, "  %d", reg_PC);
	fprintf(pLog, "      %d", reg_VTSYN);
	fprintf(pLog, "     %d", reg_CAS322);
	fprintf(pLog, "     %d", reg_RASEL);
	fprintf(pLog, "     %d", reg_TRPLY);
	fprintf(pLog, "     %02d", nHGateCounter);
	fprintf(pLog, "  %05o", reg_VA);
	fprintf(pLog, "  %02d", nVGateCounter);
	fprintf(pLog, "  %06o", reg_LC);
	fprintf(pLog, "  %d", m_IOPins.PIN_CAS & 3);
	fprintf(pLog, "   %d", m_IOPins.PIN_RAS);
	fprintf(pLog, "  %d", m_IOPins.PIN_WE);
	fprintf(pLog, "   %d", m_IOPins.PIN_WTD);
	fprintf(pLog, "   %d", m_IOPins.PIN_WTI);
	fprintf(pLog, "    %d", m_IOPins.PIN_SINCO);
	fprintf(pLog, "\n");
	fflush(pLog);
}

void CVP1_037_014_BK11::OutLog(int nClk, const char *str)
{
	static int nCount = 0;

	if (!((nCount++) % 20))
	{
		fprintf(pLog, "    clk   \\PC\\VTSYN\\CAS322/RASEL*TRPLY\\HGATE\\VA  \\VGATE\\LC");
		fprintf(pLog, "    CAS RAS WE WTD WTI SINCO");
		fprintf(pLog, " SYNC DIN DOUT wRPLout wRPLreg");
		fprintf(pLog, " nIRQ2 PIN_A  Window D28CA D28QA\n");
	}

	fprintf(pLog, "%s %08d", str, nClk);
	fprintf(pLog, "  %d", reg_PC);
	fprintf(pLog, "    %d", reg_VTSYN);
	fprintf(pLog, "     %d", reg_CAS322);
	fprintf(pLog, "     %d", reg_RASEL);
	fprintf(pLog, "     %d", reg_TRPLY);
	fprintf(pLog, "     %02d", nHGateCounter);
	fprintf(pLog, "  %05o", reg_VA);
	fprintf(pLog, "  %02d", nVGateCounter);
	fprintf(pLog, "  %06o", reg_LC);
	fprintf(pLog, "  %d", m_IOPins.PIN_CAS & 3);
	fprintf(pLog, "   %d", m_IOPins.PIN_RAS);
	fprintf(pLog, "  %d", m_IOPins.PIN_WE);
	fprintf(pLog, "   %d", m_IOPins.PIN_WTD);
	fprintf(pLog, "   %d", m_IOPins.PIN_WTI);
	fprintf(pLog, "    %d", m_IOPins.PIN_SINCO);
	fprintf(pLog, "    %d", wire_sync_in);
	fprintf(pLog, "    %d", wire_din_in);
	fprintf(pLog, "    %d", wire_dout_in);
	fprintf(pLog, "      %d", wire_rply_out);
	fprintf(pLog, "      %d", wire_rply_reg);
	fprintf(pLog, "     %d", m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nIRQ2));
	fprintf(pLog, "    %06o", m_IOPins.PIN_A);
	fprintf(pLog, "    %d", reg_Window);
	fprintf(pLog, "      %d", wire_D28C_A);
	fprintf(pLog, "    %d", reg_D28Q_A);
	fprintf(pLog, "\n");
	fflush(pLog);
}
#endif
