#include "pch.h"
#include "AZBK.h"
#include "Platform.h"

/////////////////////////////////////////////////////////////////////////////////
//
// AZ Screen
//
/////////////////////////////////////////////////////////////////////////////////

const std::vector<int> CAZBK::m_vAZBPP = { 1, 2, 4, 8 };
const std::vector<int> CAZBK::m_vAZLLEN = { 32 * 2, 64 * 2, 128 * 2, 256 * 2 }; // ширина строки в байтах
const std::vector<int> CAZBK::m_vAZXS = { 1, 2, 4, 8 };
const std::vector<int> CAZBK::m_vAZYS = { 1, 2, 3, 4 };

const std::vector<int> CAZBK::m_vAZLSCRL = { 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144 };


uint32_t CAZBK::R5G5B5toRGB(uint16_t c) const
{
	uint8_t b = (c & 0x1f) << 3;
	uint8_t g = ((c >> 5) & 0x1f) << 3;
	uint8_t r = ((c >> 10) & 0x1f) << 3;
	return 0xff000000 | (uint32_t(r) << 16) | (uint32_t(g) << 8) | b;
}

uint16_t CAZBK::RGBtoR5G5B5(uint32_t c) const
{
	uint8_t b = (c & 0xff) >> 3;
	uint8_t g = ((c >> 8) & 0xff) >> 3;
	uint8_t r = ((c >> 16) & 0xff) >> 3;
	return (uint16_t(r) << 10) | (uint16_t(g) << 5) | b;
}

void CAZBK::SetScreenParam(uint16_t v)
{
	std::lock_guard<std::mutex> lk(m_mutScrParam);
	m_AZData.nAZVideoMode = v & 7;
	m_AZData.nAZBpp = m_vAZBPP[v & 3];
	m_AZData.nAZLineLen = m_vAZLLEN[(v >> 3) & 3];
	m_AZData.nAZXStretch = m_vAZXS[(v >> 6) & 3];
	m_AZData.nAZYStretch = m_vAZYS[(v >> 9) & 3];
	int idx = (v >> 12) & 0x0f;

	if (idx > 11)
	{
		idx = 11;
	}

	m_AZData.nAZScrlSize = m_vAZLSCRL[idx];
	// теперь посчитаем сколько строк будет рулон
	m_AZData.nAZScrlLines = (m_AZData.nAZScrlSize * 2 / m_AZData.nAZLineLen);
	// сбрасываем кадр. Чтобы он заново начинался
	//m_AZData.nCurScrLineNum = 0;

/*
// Сохраним это на память. чтобы палитры получить
#pragma warning(disable:4996)
// сделаем уникальное имя файла, чтоб не заморачиваться - возьмём тек. время
	constexpr auto nBufSize = 80;
	TCHAR       buf[nBufSize];
	struct tm   tstruct {};
	time_t      now = time(nullptr);
	tstruct = *localtime(&now);
	wcsftime(buf, nBufSize, _T("%Y%m%d-%H%M%S"), &tstruct);
	CString name = _T("paltxt_")+ CString(buf) + _T(".txt");

	FILE *fo = _tfopen(name, _T("wt"));
	int n = 0;
	for (unsigned int i : m_azPalette)
	{
		fprintf(fo, "0x%08x,", i);
		n++;
		if (n >= 16)
		{
			n = 0;
			fprintf(fo, "\n");
		}
	}
	fclose(fo);
*/
}

// фиксация регистров и параметров на время формирования кадра
void CAZBK::FixFrameParameters()
{
	// копия данных на время работы фрейма
	m_AZData.nFrameAZVideoMode = m_AZData.nAZVideoMode;  // номер видеорежима
	m_AZData.nFrameAZLineLen   = m_AZData.nAZLineLen;    // длина строки в байтах
	m_AZData.nFrameAZXStretch  = m_AZData.nAZXStretch;   // число повторов точки по ширине
	m_AZData.nFrameAZYStretch  = m_AZData.nAZYStretch;   // число повторов строки по высоте
	m_AZData.nFrameAZScrlLines = m_AZData.nAZScrlLines;  // размер рулона в строках.
	// копия регистров скроллинга
	m_AZData.nFrameVSCRL177244 = m_AZRegs.nVSCRL177244;
	m_AZData.nFrameVSCRL177246 = m_AZRegs.nVSCRL177246;
	m_AZData.nFrameVSCRL177250 = m_AZRegs.nVSCRL177250;
	m_AZData.nFrameHSCRL177252 = m_AZRegs.nHSCRL177252;
	m_AZData.nFrameHSCRL177254 = m_AZRegs.nHSCRL177254;
	m_AZData.nFrameHSCRL177256 = m_AZRegs.nHSCRL177256;
}


// функция подготовки одной линии экрана
void CAZBK::MakeScreenLine()
{
	std::lock_guard<std::mutex> lk(m_mutScrParam);

	// перед выполнением действий
	// если номер линии 0 - нужно инициализировать переменные
	if (m_AZData.nCurScrLineNum == 0)
	{
		FixFrameParameters(); // фиксируем параметры на время кадра
		m_AZData.pCurTextureBits = m_pTexture;
		m_AZData.nCurScrLinesCount = 0;
		m_AZData.nCurScrYStretch = 0;
		size_t nOffs = AZ_BANK_TO_OFFSET(m_AZRegs.nSCRPageNum_L0); // это смещение до начала экрана в памяти
		m_AZData.pDataBase_L0 = m_pMemory.data() + nOffs; // вот отсюда начинаются данные верхнего слоя
		nOffs = AZ_BANK_TO_OFFSET(m_AZRegs.nSCRPageNum_L1); // это смещение до начала экрана в памяти
		m_AZData.pDataBase_L1 = m_pMemory.data() + nOffs; // вот отсюда начинаются данные среднего слоя
		nOffs = AZ_BANK_TO_OFFSET(m_AZRegs.nSCRPageNum_L2); // это смещение до начала экрана в памяти
		m_AZData.pDataBase_L2 = m_pMemory.data() + nOffs; // вот отсюда начинаются данные нижнего слоя
	}

	// если в пределах видимой строки, то её рисовать
	if ((unsigned int)m_AZData.nCurScrLineNum < 768)
	{
		if (m_AZData.bAZExtended && m_AZData.nCurScrLineNum > 192)
		{
			memset(&m_AZData.LineBuffer[0], 0, 1024 * sizeof(uint32_t));
		}
		else if (m_AZData.nCurScrYStretch == 0) // если ещё не повтор строки, то её надо формировать
		{
			int nWidth = 1024;         // фиксированная реальная ширина в точках
			uint32_t *pBits = &m_AZData.LineBuffer[0]; // указатель на буфер

			switch (m_AZData.nFrameAZVideoMode)
			{
				default:
				case 0: // 1 бит на цвет (2 цвета)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_2_COLOR_IDX]; // начало палитры
					uint8_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines);

					// !!! Сейчас мы ограничиваем скроллинг шириной строки экрана.
					// !!! А в реальности наверное нужно ограничивать размером значения скроллинга - 256
					int idx = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						uint8_t w = pData[idx]; // берём очередной байт
						idx = ++idx % m_AZData.nFrameAZLineLen;

						for (int i = 8; i > 0; i--) // 8 бит
						{
							const size_t p = w & 1; w >>= 1;
							const uint32_t px = *(pPaletteIdx + p);
							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
							{
								*pBits++ = px; // рисуем точку

								if (!(--nWidth))
								{
									goto l_endl0; // если нарисовали всю строку, то хватит
								}
							}
						}
					}

l_endl0:
					break;
				}
				case 1: // 2 бита на цвет (4 цвета)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_4_COLOR_IDX + m_AZData.nLegacyPalette * 4];
					const uint8_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines);

					int idx = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						uint8_t w = pData[idx]; // берём очередной байт
						idx = ++idx % m_AZData.nFrameAZLineLen;

						for (int i = 4; i > 0; i--) // 4 двухбития
						{
							const size_t p = w & 0x3; w >>= 2;
							const uint32_t px = *(pPaletteIdx + p);
							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
							{
								*pBits++ = px;  // рисуем точку

								if (!(--nWidth))
								{
									goto l_endl1; // если нарисовали всю строку, то хватит
								}
							}
						}
					}

l_endl1:
					break;
				}
				case 2: // 4 бита на цвет (16 цветов)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_16_COLOR_IDX]; // начало палитры
					const uint8_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines);

					int idx = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						uint8_t w = pData[idx]; // берём очередной байт
						idx = ++idx % m_AZData.nFrameAZLineLen;

						for (int i = 2; i > 0; i--)    // 2 четырёхбития
						{
							const size_t p = w & 0xf; w >>= 4;
							const uint32_t px = *(pPaletteIdx + p);
							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
							{
								*pBits++ = px;  // рисуем точку

								if (!(--nWidth))
								{
									goto l_endl2; // если нарисовали всю строку, то хватит
								}
							}
						}
					}

l_endl2:
					break;
				}
				case 3: // 8 бит на цвет (256 цветов)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_256_COLOR_IDX]; // начало палитры
					const uint8_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines);

					int idx = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						const size_t p = pData[idx]; // берём очередной байт
						idx = ++idx % m_AZData.nFrameAZLineLen;

						const uint32_t px = *(pPaletteIdx + p);
						// повторим точку нужное количество раз
						for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
						{
							*pBits++ = px;  // рисуем точку

							if (!(--nWidth))
							{
								goto l_endl3; // если нарисовали всю строку, то хватит
							}
						}
					}

l_endl3:
					break;
				}
				case 4: // 1 бит на цвет (8 цветов, сборный "слоеный" режим)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_256_COLOR_IDX]; // начало палитры
					const uint8_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines); // верхний слой
					const uint8_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameAZScrlLines); // средний слой
					const uint8_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameAZScrlLines); // нижний слой

					int idxL0 = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL1 = m_AZData.nFrameHSCRL177254 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL2 = m_AZData.nFrameHSCRL177256 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						uint8_t w = pDataL0[idxL0]; // берём очередной байт
						uint8_t w2 = pDataL1[idxL1]; // берём очередной байт
						uint8_t w3 = pDataL2[idxL2]; // берём очередной байт
						idxL0 = ++idxL0 % m_AZData.nFrameAZLineLen;
						idxL1 = ++idxL1 % m_AZData.nFrameAZLineLen;
						idxL2 = ++idxL2 % m_AZData.nFrameAZLineLen;

						for (int i = 8; i > 0; i--) // 8 бит
						{
							size_t p = (static_cast<size_t>(w & 1)) << 2; w >>= 1;
							p |= (static_cast<size_t>(w2 & 1)) << 1; w2 >>= 1;
							p |= (w3 & 1); w3 >>= 1;
							const uint32_t px = *(pPaletteIdx + p);
							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
							{
								*pBits++ = px; // рисуем точку

								if (!(--nWidth))
								{
									goto l_endl4; // если нарисовали всю строку, то хватит
								}
							}
						}
					}

l_endl4:
					break;
				}
				case 5: // 2 бита на цвет (4 цвета, включены слои)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_4_COLOR_IDX + m_AZData.nLegacyPalette * 4];
					const uint32_t *pPaletteIdx2 = &m_azPalette[AZ_4_COLOR_IDX + ((m_AZData.nLegacyPalette + 1) & 0xf) * 4];
					const uint32_t *pPaletteIdx3 = &m_azPalette[AZ_4_COLOR_IDX + ((m_AZData.nLegacyPalette + 2) & 0xf) * 4];
					const uint8_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines); // верхний слой
					const uint8_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameAZScrlLines); // средний слой
					const uint8_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameAZScrlLines); // нижний слой

					int idxL0 = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL1 = m_AZData.nFrameHSCRL177254 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL2 = m_AZData.nFrameHSCRL177256 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						uint8_t w = pDataL0[idxL0]; // берём очередной байт
						uint8_t w2 = pDataL1[idxL1]; // берём очередной байт
						uint8_t w3 = pDataL2[idxL2]; // берём очередной байт
						idxL0 = ++idxL0 % m_AZData.nFrameAZLineLen;
						idxL1 = ++idxL1 % m_AZData.nFrameAZLineLen;
						idxL2 = ++idxL2 % m_AZData.nFrameAZLineLen;

						for (int i = 4; i > 0; i--) // 4 двухбития
						{
							const size_t p = w & 0x3;
							const size_t p2 = w2 & 0x3;
							const size_t p3 = w3 & 0x3;
							const uint32_t px = p ? *(pPaletteIdx + p) : (p2 ? *(pPaletteIdx2 + p2) : *(pPaletteIdx3 + p3));
							w >>= 2; w2 >>= 2; w3 >>= 2;
							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
							{
								*pBits++ = px;  // рисуем точку

								if (!(--nWidth))
								{
									goto l_endl5; // если нарисовали всю строку, то хватит
								}
							}
						}
					}

l_endl5:
					break;
				}
				case 6: // 4 бита на цвет (16 цветов, включены слои)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_256_COLOR_IDX]; // начало палитры
					const uint32_t *pPaletteIdx2 = &m_azPalette[AZ_256_COLOR_IDX + 16]; // начало палитры
					const uint32_t *pPaletteIdx3 = &m_azPalette[AZ_256_COLOR_IDX + 32]; // начало палитры
					const uint8_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines); // верхний слой
					const uint8_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameAZScrlLines); // средний слой
					const uint8_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameAZScrlLines); // нижний слой

					int idxL0 = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL1 = m_AZData.nFrameHSCRL177254 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL2 = m_AZData.nFrameHSCRL177256 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						uint8_t w = pDataL0[idxL0]; // берём очередной байт
						uint8_t w2 = pDataL1[idxL1]; // берём очередной байт
						uint8_t w3 = pDataL2[idxL2]; // берём очередной байт
						idxL0 = ++idxL0 % m_AZData.nFrameAZLineLen;
						idxL1 = ++idxL1 % m_AZData.nFrameAZLineLen;
						idxL2 = ++idxL2 % m_AZData.nFrameAZLineLen;

						for (int i = 2; i > 0; i--)    // 2 четырёхбития
						{
							const size_t p = w & 0xf;
							const size_t p2 = w2 & 0xf;
							const size_t p3 = w3 & 0xf;
							const uint32_t px = p ? *(pPaletteIdx + p) : (p2 ? *(pPaletteIdx2 + p2) : *(pPaletteIdx3 + p3));
							w >>= 4; w2 >>= 4; w3 >>= 4;
							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
							{
								*pBits++ = px;  // рисуем точку

								if (!(--nWidth))
								{
									goto l_endl6; // если нарисовали всю строку, то хватит
								}
							}
						}
					}

l_endl6:
					break;
				}
				case 7: // 8 бит на цвет (256 цветов, включены слои)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_256_COLOR_IDX]; // начало палитры
					const uint8_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameAZScrlLines); // верхний слой
					const uint8_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameAZScrlLines); // средний слой
					const uint8_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameAZLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameAZScrlLines); // нижний слой

					int idxL0 = m_AZData.nFrameHSCRL177252 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL1 = m_AZData.nFrameHSCRL177254 % m_AZData.nFrameAZLineLen; // текущий индекс
					int idxL2 = m_AZData.nFrameHSCRL177256 % m_AZData.nFrameAZLineLen; // текущий индекс

					for (int c = m_AZData.nFrameAZLineLen; c > 0; c--) // длина линии в байтах
					{
						const size_t p = pDataL0[idxL0]; // берём очередной байт
						const size_t p2 = pDataL1[idxL1]; // берём очередной байт
						const size_t p3 = pDataL2[idxL2]; // берём очередной байт
						idxL0 = ++idxL0 % m_AZData.nFrameAZLineLen;
						idxL1 = ++idxL1 % m_AZData.nFrameAZLineLen;
						idxL2 = ++idxL2 % m_AZData.nFrameAZLineLen;

						const uint32_t px = p ? *(pPaletteIdx + p) : (p2 ? *(pPaletteIdx + p2) : *(pPaletteIdx + p3));
						// повторим точку нужное количество раз
						for (int sx = m_AZData.nFrameAZXStretch; sx > 0; sx--)
						{
							*pBits++ = px;  // рисуем точку

							if (!(--nWidth))
							{
								goto l_endl7; // если нарисовали всю строку, то хватит
							}
						}
					}
l_endl7:
					break;
				}
			}
		}
		// переносим сформированную строку из буфера в текстуру
		memcpy(m_AZData.pCurTextureBits, &m_AZData.LineBuffer[0], 1024 * sizeof(uint32_t));
		m_AZData.pCurTextureBits += 1024;
		// обработаем счётчик повтора строк
		if (++m_AZData.nCurScrYStretch >= m_AZData.nFrameAZYStretch)
		{
			m_AZData.nCurScrYStretch = 0;
			m_AZData.nCurScrLinesCount++;
		}
	}

	m_AZData.nCurScrLineNum++;

	// после выполнения действий
	// если номер линии 767 - нужно принимать параметры блиттера
	if (m_AZData.nCurScrLineNum == 767)
	{
		m_pAZBlt->DoPrepare(m_AZRegs.nBLTC177270, AZ_BANK_TO_OFFSET(m_AZRegs.nBLTP177272));
	}

	// если номер линии 769 - нужно отображать кадр, запускать блиттер и генерировать кадровое прерывание
	if (m_AZData.nCurScrLineNum == 769)
	{
		m_pAZBlt->DoExec();

		//g_Platform.GetMainPtr()->PostMessage(WM_SCR_DRAW, 1);
		g_Platform.GetScreenPtr()->DrawScreen(true, 1); // рисуем напрямую. Так быстрее, но в Direct2D тормозит, когда окно свёрнуто.
		// если включен таймер и режим 60Гц - делаем прерывание
		if ((m_AZRegs.nAZ_mapper_ctrl & (AZ_MMU_V100_ON | AZ_MMU_50HZ_TIMER)) == AZ_MMU_V100_ON)
		{
			g_Platform.GetCPUPtr()->TickIRQ2();
		}
	}

	// если номер линии 806, то номер линии 0
	if (m_AZData.nCurScrLineNum == 769)
	{
		m_AZData.nCurScrLineNum = 0;
	}
}

void CAZBK::Stop50НzThread()
{
	if (m_lock50НzThr.IsLocked()) // если поток запущен
	{
		m_lock50Нz.Lock();

		do
		{
			SleepEx(10, FALSE);
		}
		while (m_lock50НzThr.IsLocked());
	}
}

bool CAZBK::Start50НzThread()
{
	if (!m_lock50НzThr.IsLocked()) // если поток ещё не запущен
	{
		m_50НzThread = std::thread(&CAZBK::Clk50НzThreadFunc, this);

		if (m_50НzThread.joinable())
		{
			m_50НzThread.detach();
			return true;
		}
	}

	return false;
}

void CAZBK::Clk50НzThreadFunc() const
{
	DWORD_PTR oldmask = ::SetThreadAffinityMask(::GetCurrentThread(), 1);
	LARGE_INTEGER liDueTime{};    //интервал таймера
	liDueTime.QuadPart = -10000000LL / 48;
	// создаём waitable timer.
	HANDLE hWaitableTimer = ::CreateWaitableTimer(nullptr, FALSE, nullptr);

	if (hWaitableTimer)
	{
		m_lock50НzThr.Lock();

		do
		{
			// Устанавливаем таймер.
			::SetWaitableTimer(hWaitableTimer, &liDueTime, 0, nullptr, nullptr, FALSE);
			// ждём срабатывания таймера.
			::WaitForSingleObject(hWaitableTimer, INFINITE);

			// если включен таймер и режим 50Гц - делаем прерывание
			if ((m_AZRegs.nAZ_mapper_ctrl & (AZ_MMU_V100_ON | AZ_MMU_50HZ_TIMER)) == (AZ_MMU_V100_ON | AZ_MMU_50HZ_TIMER))
			{
				g_Platform.GetCPUPtr()->TickIRQ2();
			}
		}
		while (!m_lock50Нz.IsLocked());       //пока не придёт событие остановки

		::CancelWaitableTimer(hWaitableTimer);
		::CloseHandle(hWaitableTimer);
		m_lock50Нz.UnLock(); //выставляем событие, что таймер остановился, и можно продолжать остальные действия по остановке
		m_lock50НzThr.UnLock();
	}

	::SetThreadAffinityMask(::GetCurrentThread(), oldmask);
}
