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

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

const int CAZBK::m_vAZBPP[] = {1, 2, 4, 8};
const int CAZBK::m_vAZLLEN[] = {32, 64, 128, 256}; // ширина строки в словах
const int CAZBK::m_vAZXS[] = {1, 2, 4, 8};
const int CAZBK::m_vAZYS[] = {1, 2, 3, 4};

const int CAZBK::m_vAZLSCRL[] = {6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304, 131072, 196608, 262144};


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

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

void CAZBK::SetScreenParam(const uint16_t v)
{
	std::lock_guard<std::mutex> lk(m_mutScrParam);
	m_AZData.nVideo_Mode = v & 7;
	m_AZData.nBpp = m_vAZBPP[v & 3];
	m_AZData.nLineLen = m_vAZLLEN[(v >> 3) & 3];
	m_AZData.nXStretch = m_vAZXS[(v >> 6) & 3];
	m_AZData.nYStretch = m_vAZYS[(v >> 9) & 3];
	int idx = (v >> 12) & 0x0f;

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

	m_AZData.nScrlSize = m_vAZLSCRL[idx];
	// теперь посчитаем сколько строк будет рулон
	m_AZData.nScrlLines = (m_AZData.nScrlSize / m_AZData.nLineLen);

	if (v & AZ_SCR_SYNCRO)
	{
		SetSynchroMode();
	}

	/*
	// Сохраним это на память. чтобы палитры получить
	#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::SetSynchroMode()
{
	m_AZData.nFrameHSCRL177252 = m_AZRegs.nHSCRL177252;
	m_AZData.nFrameHSCRL177254 = m_AZRegs.nHSCRL177254;
	m_AZData.nFrameHSCRL177256 = m_AZRegs.nHSCRL177256;
	m_AZData.nFrameSCRPageNum_L0 = m_AZRegs.nSCRPageNum_L0;
	m_AZData.nFrameSCRPageNum_L1 = m_AZRegs.nSCRPageNum_L1;
	m_AZData.nFrameSCRPageNum_L2 = m_AZRegs.nSCRPageNum_L2;
}


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

	// перед выполнением действий
	// если номер линии 0 - нужно инициализировать переменные
	if (m_AZData.nCurScrLineNum == 0)
	{
		// запоимнаем режим синхронизации
		m_AZData.bMode_Sync = !!(m_AZRegs.nSCRCSR & AZ_SCR_SYNCRO);
		// фиксируем параметры на время кадра
		// копия данных на время работы фрейма
		m_AZData.nFrameVideo_Mode = m_AZData.nVideo_Mode;  // номер видеорежима
		m_AZData.nFrameLineLen = m_AZData.nLineLen;    // длина строки в байтах
		m_AZData.nFrameXStretch = m_AZData.nXStretch;   // число повторов точки по ширине
		m_AZData.nFrameYStretch = m_AZData.nYStretch;   // число повторов строки по высоте
		m_AZData.nFrameScrlLines = m_AZData.nScrlLines;  // размер рулона в строках.
		// копия регистров скроллинга
		m_AZData.nFrameVSCRL177244 = m_AZRegs.nVSCRL177244;
		m_AZData.nFrameVSCRL177246 = m_AZRegs.nVSCRL177246;
		m_AZData.nFrameVSCRL177250 = m_AZRegs.nVSCRL177250;

		if (m_AZData.bMode_Sync)
		{
			SetSynchroMode();
		}

		m_AZData.pCurTextureBits = m_pTexture;
		m_AZData.nCurScrLinesCount = 0;
		m_AZData.nCurScrYStretch = 0;
	}

	// если в пределах видимой строки, то её рисовать
	if ((unsigned int)m_AZData.nCurScrLineNum < 768)
	{
		size_t nOffs = AZ_BANK_TO_OFFSET(m_AZData.bMode_Sync ? m_AZData.nFrameSCRPageNum_L0 : m_AZRegs.nSCRPageNum_L0); // это смещение до начала экрана в памяти
		m_AZData.pDataBase_L0 = reinterpret_cast<uint16_t *>(m_pMemory.data() + nOffs); // вот отсюда начинаются данные верхнего слоя
		nOffs = AZ_BANK_TO_OFFSET(m_AZData.bMode_Sync ? m_AZData.nFrameSCRPageNum_L1 : m_AZRegs.nSCRPageNum_L1); // это смещение до начала экрана в памяти
		m_AZData.pDataBase_L1 = reinterpret_cast<uint16_t *>(m_pMemory.data() + nOffs); // вот отсюда начинаются данные среднего слоя
		nOffs = AZ_BANK_TO_OFFSET(m_AZData.bMode_Sync ? m_AZData.nFrameSCRPageNum_L2 : m_AZRegs.nSCRPageNum_L2); // это смещение до начала экрана в памяти
		m_AZData.pDataBase_L2 = reinterpret_cast<uint16_t *>(m_pMemory.data() + nOffs); // вот отсюда начинаются данные нижнего слоя

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

			switch (m_AZData.nFrameVideo_Mode)
			{
				default:
				case 0: // 1 бит на цвет (2 цвета)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_2_COLOR_IDX]; // начало палитры
					uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines);
					uint8_t idx = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 16; i > 0; i--) // 16 бит
						{
							const size_t p = w & 1; w >>= 1;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameXStretch; 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 uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines);
					uint8_t idx = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 8; i > 0; i--) // 8 двухбитий
						{
							const size_t p = w & 0x3; w >>= 2;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameXStretch; 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 uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines);
					uint8_t idx = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 4; i > 0; i--)    // 4 четырёхбития
						{
							const size_t p = w & 0xf; w >>= 4;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameXStretch; 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 uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines);
					uint8_t idx = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 2; i > 0; i--)    // 2 байта
						{
							const size_t p = w & 0xff; w >>= 8;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameScrlLines); // нижний слой
					uint8_t idxL0 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL1 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL2 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 16; i > 0; i--) // 16 бит
						{
							size_t p = (static_cast<size_t>(w1 & 1)) << 2; w1 >>= 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.nFrameXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameScrlLines); // нижний слой
					uint8_t idxL0 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL1 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL2 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 8; i > 0; i--) // 8 двухбитий
						{
							const size_t p1 = w1 & 0x3;
							const size_t p2 = w2 & 0x3;
							const size_t p3 = w3 & 0x3;
							const uint32_t px = p1 ? *(pPaletteIdx + p1) : (p2 ? * (pPaletteIdx2 + p2) : * (pPaletteIdx3 + p3));
							w1 >>= 2; w2 >>= 2; w3 >>= 2;

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameScrlLines); // нижний слой
					uint8_t idxL0 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL1 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL2 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 4; i > 0; i--)    // 4 четырёхбития
						{
							const size_t p1 = w1 & 0xf;
							const size_t p2 = w2 & 0xf;
							const size_t p3 = w3 & 0xf;
							const uint32_t px = p1 ? *(pPaletteIdx + p1) : (p2 ? * (pPaletteIdx2 + p2) : * (pPaletteIdx3 + p3));
							w1 >>= 4; w2 >>= 4; w3 >>= 4;

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177250) % m_AZData.nFrameScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177246) % m_AZData.nFrameScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nFrameLineLen * ((static_cast<size_t>(m_AZData.nCurScrLinesCount) + m_AZData.nFrameVSCRL177244) % m_AZData.nFrameScrlLines); // нижний слой
					uint8_t idxL0 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL1 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nFrameLineLen; // текущий индекс
					uint8_t idxL2 = (m_AZData.bMode_Sync ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nFrameLineLen; // текущий индекс

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

						for (int i = 2; i > 0; i--)    // 2 байта
						{
							const size_t p1 = w1 & 0xff; w1 >>= 8;
							const size_t p2 = w2 & 0xff; w2 >>= 8;
							const size_t p3 = w3 & 0xff; w3 >>= 8;
							const uint32_t px = p1 ? *(pPaletteIdx + p1) : (p2 ? * (pPaletteIdx + p2) : * (pPaletteIdx + p3));

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nFrameXStretch; 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.nFrameYStretch)
		{
			m_AZData.nCurScrYStretch = 0;
			m_AZData.nCurScrLinesCount++;
		}
	}

	m_AZData.nCurScrLineNum++;
	m_pAZBlt->exec_chunk_cmd();

	// если номер линии 769 - нужно отображать кадр, запускать блиттер и генерировать кадровое прерывание
	if (m_AZData.nCurScrLineNum == 769)
	{
		if (!m_pAZBlt->isRun())
		{
			const int cmd = (m_AZRegs.nBLTC177270 & 0x7f);

			if (cmd && !(m_AZRegs.nBLTC177270 & AZ_BLT_MANUAL))
			{
				m_pAZBlt->DoExec(cmd, AZ_BANK_TO_OFFSET(m_AZRegs.nBLTP177272));
			}
		}

		//g_Platform.GetMainPtr()->PostMessage(WM_SCR_DRAW, static_cast<int>(CScreen::SCR_TYPE::AZBK));
		g_Platform.GetScreenPtr()->DrawScreen(true, CScreen::SCR_TYPE::AZBK); // рисуем напрямую. Так быстрее, но в 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 >= 806)
	{
		m_AZData.nCurScrLineNum = 0;
	}
}

void CAZBK::CatchAr2Lat()
{
	if (m_AZRegs.nSCRCSR == 012201)
	{
		SetSystemRegister(REG_SCR_CSR, 012100, GSSR_NONE);
	}
	else if (m_AZRegs.nSCRCSR == 012100)
	{
		SetSystemRegister(REG_SCR_CSR, 012201, GSSR_NONE);
	}
}


bool CAZBK::Clk50НzFunc() const
{
	return m_bAZ50HzEnabled &&
	       ((m_AZRegs.nAZ_mapper_ctrl & (AZ_MMU_V100_ON | AZ_MMU_50HZ_TIMER)) == (AZ_MMU_V100_ON | AZ_MMU_50HZ_TIMER));
}


void CAZBK::DrawDebugScreen()
{
	std::lock_guard<std::mutex> lk(m_mutScrParam);
	uint32_t LineBuffer[1024] {};  // буфер строки
	uint32_t *pCurTextureBits = m_pTexture;
	int nCurScrLinesCount = 0;
	int nCurScrYStretch = 0;
	const bool bSyncro = !!(m_AZRegs.nSCRCSR & AZ_SCR_SYNCRO);
	size_t nOffs = AZ_BANK_TO_OFFSET(bSyncro ? m_AZData.nFrameSCRPageNum_L0 : m_AZRegs.nSCRPageNum_L0); // это смещение до начала экрана в памяти
	m_AZData.pDataBase_L0 = reinterpret_cast<uint16_t *>(m_pMemory.data() + nOffs); // вот отсюда начинаются данные верхнего слоя
	nOffs = AZ_BANK_TO_OFFSET(bSyncro ? m_AZData.nFrameSCRPageNum_L1 : m_AZRegs.nSCRPageNum_L1); // это смещение до начала экрана в памяти
	m_AZData.pDataBase_L1 = reinterpret_cast<uint16_t *>(m_pMemory.data() + nOffs); // вот отсюда начинаются данные среднего слоя
	nOffs = AZ_BANK_TO_OFFSET(bSyncro ? m_AZData.nFrameSCRPageNum_L2 : m_AZRegs.nSCRPageNum_L2); // это смещение до начала экрана в памяти
	m_AZData.pDataBase_L2 = reinterpret_cast<uint16_t *>(m_pMemory.data() + nOffs); // вот отсюда начинаются данные нижнего слоя

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

			switch (m_AZData.nVideo_Mode)
			{
				default:
				case 0: // 1 бит на цвет (2 цвета)
				{
					const uint32_t *pPaletteIdx = &m_azPalette[AZ_2_COLOR_IDX]; // начало палитры
					uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines);
					uint8_t idx = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 16; i > 0; i--) // 16 бит
						{
							const size_t p = w & 1; w >>= 1;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nXStretch; 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 uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines);
					uint8_t idx = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 8; i > 0; i--) // 8 двухбитий
						{
							const size_t p = w & 0x3; w >>= 2;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nXStretch; 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 uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines);
					uint8_t idx = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 4; i > 0; i--)    // 4 четырёхбития
						{
							const size_t p = w & 0xf; w >>= 4;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nXStretch; 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 uint16_t *pData = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines);
					uint8_t idx = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 2; i > 0; i--)    // 2 байта
						{
							const size_t p = w & 0xff; w >>= 8;
							const uint32_t px = *(pPaletteIdx + p);

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177246) % m_AZData.nScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177244) % m_AZData.nScrlLines); // нижний слой
					uint8_t idxL0 = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL1 = (bSyncro ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL2 = (bSyncro ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 16; i > 0; i--) // 16 бит
						{
							size_t p = (static_cast<size_t>(w1 & 1)) << 2; w1 >>= 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.nXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177246) % m_AZData.nScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177244) % m_AZData.nScrlLines); // нижний слой
					uint8_t idxL0 = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL1 = (bSyncro ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL2 = (bSyncro ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 8; i > 0; i--) // 8 двухбитий
						{
							const size_t p1 = w1 & 0x3;
							const size_t p2 = w2 & 0x3;
							const size_t p3 = w3 & 0x3;
							const uint32_t px = p1 ? *(pPaletteIdx + p1) : (p2 ? * (pPaletteIdx2 + p2) : * (pPaletteIdx3 + p3));
							w1 >>= 2; w2 >>= 2; w3 >>= 2;

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177246) % m_AZData.nScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177244) % m_AZData.nScrlLines); // нижний слой
					uint8_t idxL0 = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL1 = (bSyncro ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL2 = (bSyncro ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 4; i > 0; i--)    // 4 четырёхбития
						{
							const size_t p1 = w1 & 0xf;
							const size_t p2 = w2 & 0xf;
							const size_t p3 = w3 & 0xf;
							const uint32_t px = p1 ? *(pPaletteIdx + p1) : (p2 ? * (pPaletteIdx2 + p2) : * (pPaletteIdx3 + p3));
							w1 >>= 4; w2 >>= 4; w3 >>= 4;

							// повторим точку нужное количество раз
							for (int sx = m_AZData.nXStretch; 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 uint16_t *pDataL0 = m_AZData.pDataBase_L0 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177250) % m_AZData.nScrlLines); // верхний слой
					const uint16_t *pDataL1 = m_AZData.pDataBase_L1 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177246) % m_AZData.nScrlLines); // средний слой
					const uint16_t *pDataL2 = m_AZData.pDataBase_L2 + m_AZData.nLineLen * ((static_cast<size_t>(nCurScrLinesCount) + m_AZRegs.nVSCRL177244) % m_AZData.nScrlLines); // нижний слой
					uint8_t idxL0 = (bSyncro ? m_AZData.nFrameHSCRL177252 : m_AZRegs.nHSCRL177252) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL1 = (bSyncro ? m_AZData.nFrameHSCRL177254 : m_AZRegs.nHSCRL177254) % m_AZData.nLineLen; // текущий индекс
					uint8_t idxL2 = (bSyncro ? m_AZData.nFrameHSCRL177256 : m_AZRegs.nHSCRL177256) % m_AZData.nLineLen; // текущий индекс

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

						for (int i = 2; i > 0; i--)    // 2 байта
						{
							const size_t p1 = w1 & 0xff; w1 >>= 8;
							const size_t p2 = w2 & 0xff; w2 >>= 8;
							const size_t p3 = w3 & 0xff; w3 >>= 8;
							const uint32_t px = p1 ? *(pPaletteIdx + p1) : (p2 ? * (pPaletteIdx + p2) : * (pPaletteIdx + p3));

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

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

l_endl7:
					break;
				}
			}
		}

		// переносим сформированную строку из буфера в текстуру
		memcpy(pCurTextureBits, &LineBuffer[0], 1024 * sizeof(uint32_t));
		pCurTextureBits += 1024;

		// обработаем счётчик повтора строк
		if (++nCurScrYStretch >= m_AZData.nYStretch)
		{
			nCurScrYStretch = 0;
			nCurScrLinesCount++;
		}

		// блиттер тоже должен работать
		m_pAZBlt->exec_chunk_cmd();
	}
}

