// rev 1.03
#include "stdafx.h"
#include "qbus_irps.h"
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <locale>

//таблица соответствия верхней половины аскии кодов с 128 по 255, включая псевдографику
static const char koi8tbl10[128] =
{
	//{200..237} этих символов на бк10 нету.
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	//{240..277}
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
	//{300..337}
	'ю', 'а', 'б', 'ц', 'д', 'е', 'ф', 'г',
	'х', 'и', 'й', 'к', 'л', 'м', 'н', 'о',
	'п', 'я', 'р', 'с', 'т', 'у', 'ж', 'в',
	'ь', 'ы', 'з', 'ш', 'э', 'щ', 'ч', 'ъ',
	//{340..377}
	'Ю', 'А', 'Б', 'Ц', 'Д', 'Е', 'Ф', 'Г',
	'Х', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О',
	'П', 'Я', 'Р', 'С', 'Т', 'У', 'Ж', 'В',
	'Ь', 'Ы', 'З', 'Ш', 'Э', 'Щ', 'Ч', 'Ъ'
};

char koi21251(uint8_t b)
{
	if (b == 0)
	{
		return 0;
	}

	if (b >= 128)
	{
		return koi8tbl10[b - 128];
	}

	return char(b);
}


QBUS_IRPS::QBUS_IRPS()
	: m_bPinVirqEna(false)
	, m_bIakiTranslate(true)
	, m_bStopInputThread(false)
	, m_bStopOutputThread(false)
	, m_bInputNotify(false)
	, m_bOutputNotify(false)
{
	InitOp();

	if (AllocConsole())
	{
		SetConsoleTitle(_T("VM1 TTY"));
		SetConsoleOutputCP(1251);
		setlocale(LC_ALL, ".1251");
		freopen("CONOUT$", "wt", stdout);
		freopen("CONOUT$", "w", stderr);
		freopen("CONIN$", "r", stdin);
		m_bConsole = true;
	}
	else
	{
		m_bConsole = false;
	}

	StartInputThread();
	StartOutputThread();
}


QBUS_IRPS::~QBUS_IRPS()
{
	if (m_bConsole)
	{
		FreeConsole();
	}

	StopInputThread();
	StopOutputThread();
}

void QBUS_IRPS::RegisterDevice(CCommonBus *pCMNB)
{
	QBUS_Device::RegisterDevice(pCMNB);
	m_pCMNB->RegisterSignal(&m_PIN_nVIRQ, CCommonBus::MPI_LINE_nIRQ0);
	m_pCMNB->RegisterSignal(&m_PIN_nIAKO, CCommonBus::MPI_LINE_nIAK2);
}

void QBUS_IRPS::getMPI() //присваиваем значения MPI -> Mem
{
	QBUS_Device::getMPI();
	m_IOPins.pin_iaki = !m_pCMNB->GetSignal(CCommonBus::MPI_LINE_nIAK0);
}

void QBUS_IRPS::setMPI() //присваиваем значения Mem -> MPI
{
	QBUS_Device::setMPI();
	m_pCMNB->SetSignal(&m_PIN_nVIRQ, m_bPinVirqEna ? !m_IOPins.pin_virq : true);  //output  сигнал запроса прерывания
	m_pCMNB->SetSignalOE(&m_PIN_nIAKO, m_IOPins.pin_iako);                         //передаём дальше по цепочке
}

void QBUS_IRPS::InputThreadFunc()
{
	m_mutInputThreadWork.lock(); //устанавливаем событие работы потока

	for (;;)
	{
		std::unique_lock<std::mutex> lk(m_mutInputMutex);
		m_cvInput.wait(lk, [&]()
		{
			return m_bInputNotify;
		});
		m_bInputNotify = false;

		if (m_bStopInputThread) //сигнал к остановке потока получен?
		{
			break;   //и выходим
		}

		std::cin >> m_chRXData;
		Sleep(20);
		m_bTTY_RXRdy = true;
	}

	m_mutInputThreadWork.unlock();
}

void QBUS_IRPS::OutputThreadFunc()
{
	m_mutOutputThreadWork.lock(); //устанавливаем событие работы потока

	for (;;)
	{
		std::unique_lock<std::mutex> lk(m_mutOutputMutex);
		m_cvOutput.wait(lk, [&]()
		{
			return m_bOutputNotify;
		});
		m_bOutputNotify = false;

		if (m_bStopOutputThread) //сигнал к остановке потока получен?
		{
			break;   //и выходим
		}

		char ch = koi21251(m_chTXData);

		std::cout << ch;
		Sleep(20);
	}

	m_mutOutputThreadWork.unlock();
}

bool QBUS_IRPS::StartInputThread()
{
	m_thrInput = std::thread(&QBUS_IRPS::InputThreadFunc, this);

	if (m_thrInput.joinable())
	{
		m_thrInput.detach();
		return true;
	}

	return false;
}

void QBUS_IRPS::StopInputThread()
{
	{
		std::lock_guard<std::mutex> lk(m_mutInputMutex);
		m_bStopInputThread = true;
		m_bInputNotify = true;
		m_cvInput.notify_one();
	}
	std::lock_guard<std::mutex> lk(m_mutInputThreadWork);
}

bool QBUS_IRPS::StartOutputThread()
{
	m_thrOutput = std::thread(&QBUS_IRPS::OutputThreadFunc, this);

	if (m_thrOutput.joinable())
	{
		m_thrOutput.detach();
		return true;
	}

	return false;
}

void QBUS_IRPS::StopOutputThread()
{
	{
		std::lock_guard<std::mutex> lk(m_mutOutputMutex);
		m_bStopOutputThread = true;
		m_bOutputNotify = true;
		m_cvOutput.notify_one();
	}
	std::lock_guard<std::mutex> lk(m_mutOutputThreadWork);
}

bool QBUS_IRPS::InitOp()
{
	m_bTTY_TXRdy = true;
	m_bTTY_RXRdy = false;
	m_bTTY_TXIntrEna_prev = false;
	m_bTTY_RXIntrEna_prev = false;
	m_bTTY_TXIntrEna = false;
	m_bTTY_RXIntrEna = false;
	m_bTTY_RXVirq = false;
	m_bTTY_TXVirq = false;
	m_chTXData = 0;
	m_chRXData = 0;
	m_nVirqState = VIRQSTATE_NONE;
	m_bPinVirqEna = false;
	m_bRplyOut_Ena = false;
	m_bAdOut_Ena = false;
	m_bIakiTranslate = true;
	m_nRXRdyDelay = 0;
	return true;
}

bool QBUS_IRPS::DecodeAddr()
{
	switch (m_nAddr)
	{
		case 0177560:
		case 0177562:
		case 0177564:
		case 0177566:
			return true;
	}

	return false;
}

bool QBUS_IRPS::ReadOp()
{
	switch (m_nAddr)
	{
		case 0177560:
			m_IOPins.pin_ad = (m_bTTY_RXRdy ? 0200 : 0) | (m_bTTY_RXIntrEna ? 0100 : 0);
			break;

		case 0177562:
			m_bTTY_RXRdy = false;
			{
				std::lock_guard<std::mutex> lk(m_mutInputMutex);
				m_bInputNotify = true;
				m_cvInput.notify_one();
			}
			Sleep(10);
			{
				//подождём, пока идёт приём
				std::lock_guard<std::mutex> lk(m_mutInputMutex);
			}
			m_bTTY_RXVirq = (m_bTTY_RXRdy && m_bTTY_RXIntrEna);
			m_IOPins.pin_ad = m_chRXData & 0377;
			break;

		case 0177564:
			m_IOPins.pin_ad = (m_bTTY_TXRdy ? 0200 : 0) | (m_bTTY_TXIntrEna ? 0100 : 0);
			break;

		case 0177566:
			m_IOPins.pin_ad = 060;
			break;
	}

	return true;
}

bool QBUS_IRPS::WriteOp()
{
	switch (m_nAddr)
	{
		case 0177560:
			m_bTTY_RXIntrEna_prev = m_bTTY_RXIntrEna;
			m_bTTY_RXIntrEna = !!(m_IOPins.pin_ad & 0100);

			if (m_bTTY_RXRdy)
			{
				m_bTTY_RXVirq = (!m_bTTY_RXIntrEna_prev && m_bTTY_RXIntrEna);
			}

			break;

		case 0177562:
			//запись игнорируется
			break;

		case 0177564:
			m_bTTY_TXIntrEna_prev = m_bTTY_TXIntrEna;
			m_bTTY_TXIntrEna = !!(m_IOPins.pin_ad & 0100);

			if (m_bTTY_TXRdy && (!m_bTTY_TXIntrEna_prev && m_bTTY_TXIntrEna))
			{
				m_bTTY_TXVirq = m_bTTY_TXIntrEna;
			}

			break;

		case 0177566:
			if (m_bTTY_TXRdy)
			{
				m_bTTY_TXRdy = false;
				m_bTTY_TXVirq = false;
				m_nRXRdyDelay = 400;
			}

			{
				//подождём, пока идёт передача
				std::lock_guard<std::mutex> lk(m_mutOutputMutex);
			}

			m_chTXData = LOBYTE(m_IOPins.pin_ad);
			{
				//подадим сигнал к следующей передаче
				std::lock_guard<std::mutex> lk(m_mutOutputMutex);
				m_bOutputNotify = true;
				m_cvOutput.notify_one();
			}
			break;
	}

	return true;
}

void QBUS_IRPS::VIRQ_FSM()
{
	if (--m_nRXRdyDelay == 0)
	{
		m_bTTY_TXRdy = true;
		m_bTTY_TXVirq = m_bTTY_TXIntrEna;
	}
	else if (m_nRXRdyDelay < 0)
	{
		m_nRXRdyDelay = 0;
	}

	switch (m_nVirqState)
	{
		case VIRQSTATE_NONE:
		{
			if (m_bTTY_RXVirq || m_bTTY_TXVirq)
			{
				m_nVirqState = VIRQSTATE_SETVIRQ;
			}

			if (m_bIakiTranslate) //если пришло IAKO а мы его не заказывали
			{
				m_IOPins.pin_iako = m_IOPins.pin_iaki; //транслируем дальше
			}
			else
			{
				m_IOPins.pin_iako = false;
			}

			break; //ничего не делаем
		}

		case VIRQSTATE_SETVIRQ:
		{
			m_bPinVirqEna = true;
			m_IOPins.pin_virq = true;
			m_bIakiTranslate = false;
			m_nVirqState = VIRQSTATE_WAIT_IAKO;
			break;
		}

		case VIRQSTATE_WAIT_IAKO: //ждём разрешения прерывания
		{
			if (m_IOPins.pin_iaki) //дождались
			{
				m_nVirqState = VIRQSTATE_WAIT_ENDCYCLE;
			}

			break;
		}

		case VIRQSTATE_WAIT_ENDCYCLE:
		{
			if (m_nState == MPI_WAIT_SYNC_SET) //дождёмся окончания цикла обмена данными по шине
			{
				m_nVirqState = VIRQSTATE_SETRPLY;
				m_nRplyDelay = 0;
//				m_nRplyDelay = 3;
			}

			break;
		}

		case VIRQSTATE_SETRPLY:
		{
			if (--m_nRplyDelay < 0)
			{
				m_bRplyOut_Ena = true;   //выставляем сигнал RPLY
				m_bAdOut_Ena = true;
				m_IOPins.pin_ad = m_bTTY_RXVirq ? 060 : (m_bTTY_TXVirq ? 064 : 0); //и адрес
				m_nVirqState = VIRQSTATE_CLRVIRQ;
			}

			break;
		}

		case VIRQSTATE_CLRVIRQ:
		{
			m_IOPins.pin_virq = false;
			m_nVirqState = VIRQSTATE_WAIT_IAKOCLR;
			m_nRplyDelay = 0;
//			m_nRplyDelay = 1;
			break;
		}

		case VIRQSTATE_WAIT_IAKOCLR:
		{
			if (!m_IOPins.pin_iaki)
			{
				if (--m_nRplyDelay < 0)
				{
					m_bRplyOut_Ena = false;
					m_IOPins.pin_ad = 0;
					m_nVirqState = VIRQSTATE_FINAL;
				}
			}

			break;
		}

		case VIRQSTATE_FINAL: //всё приводим в начальное состояние
		{
			m_bPinVirqEna = false;
			m_bRplyOut_Ena = false;
			m_bAdOut_Ena = false;
			m_bIakiTranslate = true;
			m_nVirqState = VIRQSTATE_NONE;

			if (m_bTTY_RXVirq)
			{
				m_bTTY_RXVirq = false;
			}
			else if (m_bTTY_TXVirq)
			{
				m_bTTY_TXVirq = false;
			}

			break;
		}
	}
}

void QBUS_IRPS::eval()
{
	getMPI();

	if (m_IOPins.pin_init)
	{
		InitOp();
	}
	else
	{
		IO_FSM();
		VIRQ_FSM();
	}

	setMPI();
}

