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

#include "Platform.h"
#include "BK001001.h"

/////////////////////////////////////////////////////////////////////////////////
// данные для эмуляции СМК

constexpr auto ALTPRO_SMK_SYS_MODE = 0160;
constexpr auto ALTPRO_SMK_STD10_MODE = 060;
constexpr auto ALTPRO_SMK_OZU10_MODE = 0120;
constexpr auto ALTPRO_SMK_ALL_MODE = 020;
constexpr auto ALTPRO_SMK_STD11_MODE = 0140;
constexpr auto ALTPRO_SMK_OZU11_MODE = 040;
constexpr auto ALTPRO_SMK_HLT10_MODE = 0100;
constexpr auto ALTPRO_SMK_HLT11_MODE = 0;
// для AltPro, биты. кодирующие номер страницы
constexpr uint16_t BIT10 = 02000;
constexpr uint16_t BIT03 = 00010;
constexpr uint16_t BIT02 = 00004;
constexpr uint16_t BIT00 = 00001;


uint16_t CAZBK::AZ_716_Out() const
{
	uint16_t w = 0100200;

	if (m_AZData.bSEL1)
	{
		w |= 070000;            // если старт - 170
	}
	else
	{
		// есть доработка 037й и отключена 037ая и включена эмуляция 11М, то 140
		if ((m_AZRegs.nAZ_mapper_ctrl & (AZ_MMU_WND1_REVTYPE | AZ_MMU_037_OFF | AZ_MMU_BK11EMU_ENA)) ==
		        (AZ_MMU_WND1_REVTYPE | AZ_MMU_037_OFF | AZ_MMU_BK11EMU_ENA))
		{
			w |= 040000;
		}
	}

	return w;
}

bool CAZBK::AZ_716_In(const uint16_t w)
{
	if ((w & 04000) &&   // это флаг записи в 177716!
	        (!(m_AZRegs.nAZ_mapper_ctrl & AZ_MMU_BRDTYPE)  // и это БК11М
	         // или это БК10 + и замена памяти 037 + и есть доработка 037й
	         || ((m_AZRegs.nAZ_mapper_ctrl & (AZ_MMU_BRDTYPE | AZ_MMU_037_OFF | AZ_MMU_WND1_REVTYPE)) ==
	             (AZ_MMU_BRDTYPE | AZ_MMU_037_OFF | AZ_MMU_WND1_REVTYPE)
	            )
	        )
	   )
	{
		m_AZRegs.nMMUAZ716 = w;
		int win0_map = ((w >> 12) & 7) << 2;    // что подключено в окно 0:  40000 -  77777
		// что подключено в окно 0:  40000 -  77777
		m_AZData.pre_b11m_reg_az_mapper[0] = win0_map++;
		m_AZData.pre_b11m_reg_az_mapper[1] = win0_map++;
		m_AZData.pre_b11m_reg_az_mapper[2] = win0_map++;
		m_AZData.pre_b11m_reg_az_mapper[3] = win0_map++;

		// что подключено в окно 1: 100000 - 137777
		if (w & 2)
		{
			m_AZData.pre_b11m_reg_az_mapper[4] = 0124;
			m_AZData.pre_b11m_reg_az_mapper[5] = 0125;
			m_AZData.pre_b11m_reg_az_mapper[6] = 0122;
			m_AZData.pre_b11m_reg_az_mapper[7] = 0123;
		}
		else if (w & 1)
		{
			m_AZData.pre_b11m_reg_az_mapper[4] = 0126;
			m_AZData.pre_b11m_reg_az_mapper[5] = 0127;
			m_AZData.pre_b11m_reg_az_mapper[6] = 0130;
			m_AZData.pre_b11m_reg_az_mapper[7] = 0131;
		}
		else
		{
			int win1_map = ((w >> 8) & 7) << 2; // что подключено в окно 1: 100000 - 137777
			m_AZData.pre_b11m_reg_az_mapper[4] = win1_map++;
			m_AZData.pre_b11m_reg_az_mapper[5] = win1_map++;
			m_AZData.pre_b11m_reg_az_mapper[6] = win1_map++;
			m_AZData.pre_b11m_reg_az_mapper[7] = win1_map++;
		}

		const bool win1_roms01 = !!(w & 3); // флаг подключения ПЗУ 11М (бейсик и БОС) в окно1: 100000 - 137777
		const bool win1_roms34 = !!(w & 030);   // флаг подключения ПЗУ 11М (бейсик и БОС) в окно1: 100000 - 137777
		m_AZData.pre_b11m_reg_az_win_ctrl = 0;

		if ((win1_roms01 && (m_AZRegs.nAZ_mapper_ctrl & AZ_MMU_BK11ROM_EMU)) ||  // если были запрошены ПЗУ 11М даем их если есть разрешение
		        (!win1_roms34 && (m_AZRegs.nAZ_mapper_ctrl & AZ_MMU_037_OFF)))          // если были запрошены внешние ПЗУ11М - нету нифига!
		{
			// тогда ОЗУ и то только если оно включено
			m_AZData.pre_b11m_reg_az_win_ctrl = 0x0f00;
		}

		m_AZData.pre_b11m_reg_az_win_shdw = 0;

		if (!(win1_roms01 || win1_roms34) &&        // для любых ПЗУ нет shadow
		        !(m_AZRegs.nAZ_mapper_ctrl & AZ_MMU_037_OFF))   // если оперативка не в активном состоянии- то значит shadow;
		{
			m_AZData.pre_b11m_reg_az_win_shdw = 0x0f00;
		}

		AZ_MemoryManager();
		return true;
	}

	return false;
}

void CAZBK::AZ_MemoryManager()
{
	m_AZData.bAZAltProMM = false;
	// начальный блок статика - всегда приколочен жестко
	Set_AZ_Mapper(0, 030);
	Set_AZ_Mapper(1, 031);
	Set_AZ_Mapper(2, 032);
	Set_AZ_Mapper(3, 033);
	// окно 0 - всегда оперативка
	Set_AZ_Mapper(4, m_AZData.pre_b11m_reg_az_mapper[0]);    // 040000-047777
	Set_AZ_Mapper(5, m_AZData.pre_b11m_reg_az_mapper[1]);    // 050000-057777
	Set_AZ_Mapper(6, m_AZData.pre_b11m_reg_az_mapper[2]);    // 060000-067777
	Set_AZ_Mapper(7, m_AZData.pre_b11m_reg_az_mapper[3]);    // 070000-077777
	const uint8_t bits = (m_AZRegs.nAZ_mapper_ctrl & AZ_MMU_037_OFF) ? 0xff : 0;
	m_AZRegs.nAZ_win_ctrl &= ~0xff;
	m_AZRegs.nAZ_win_ctrl |= bits;  // если включена замена 037 - включаем оперативку как активную
	m_AZRegs.nAZ_win_shdw &= ~0xff;
	m_AZRegs.nAZ_win_shdw |= (~bits & 0xff); // если нет замены - включаем shadow
	m_AZRegs.nAZ_win_ro &= ~0xff;                     // сбрасываем на всякий случай флаг запрета записи

	if (!(m_AZRegs.nAZ_mapper_ctrl & AZ_MMU_SMK_WND1_ENA)) // нет сигнала отключения окна 1 от SMK
	{
		// окно 1 - может отключаться или эмулировать ПЗУ. адреса пишем всегда
		Set_AZ_Mapper(8, m_AZData.pre_b11m_reg_az_mapper[4]);    // 100000-107777
		Set_AZ_Mapper(9, m_AZData.pre_b11m_reg_az_mapper[5]);    // 110000-117777
		Set_AZ_Mapper(10, m_AZData.pre_b11m_reg_az_mapper[6]);    // 120000-127777
		Set_AZ_Mapper(11, m_AZData.pre_b11m_reg_az_mapper[7]);    // 130000-137777
		m_AZRegs.nAZ_win_ctrl &= ~0x0f00;
		m_AZRegs.nAZ_win_ctrl |= m_AZData.pre_b11m_reg_az_win_ctrl;
		m_AZRegs.nAZ_win_shdw &= ~0x0f00;
		m_AZRegs.nAZ_win_shdw |= m_AZData.pre_b11m_reg_az_win_shdw;
		m_AZRegs.nAZ_win_ro &= ~0x0f00;    // сбрасываем на всякий случай флаг запрета записи
	}
	else
	{
		// окно отключено через SMK - значит вставляем данные от SMK-маппера
		Set_AZ_Mapper(8, m_AZData.pre_smk_reg_az_mapper[0]); // 100000-107777
		Set_AZ_Mapper(9, m_AZData.pre_smk_reg_az_mapper[1]); // 110000-117777
		Set_AZ_Mapper(10, m_AZData.pre_smk_reg_az_mapper[2]); // 120000-127777
		Set_AZ_Mapper(11, m_AZData.pre_smk_reg_az_mapper[3]); // 130000-137777
		m_AZRegs.nAZ_win_ctrl &= ~0x0f00;
		m_AZRegs.nAZ_win_ctrl |= (m_AZData.pre_smk_reg_az_win_ctrl & 0x0f00);
		m_AZRegs.nAZ_win_shdw &= ~0x0f00;
		m_AZRegs.nAZ_win_ro &= ~0x0f00;
		m_AZRegs.nAZ_win_ro |= (m_AZData.pre_smk_reg_az_win_ro & 0x0f00);
	}

	MemoryManager(); // установим режимы доступа
}



uint16_t CAZBK::Get_AZ_Mapper(const uint32_t n)
{
	ASSERT(n < AZ_BANKS_NUM);
	return m_pMemoryMap[n].nBank;
}

void CAZBK::Set_AZ_Mapper(const uint32_t n, const uint16_t v)
{
	ASSERT(n < AZ_BANKS_NUM);
	const uint16_t w = m_pMemoryMap[n].nBank = v & 0017777;
	m_pMemoryMap[n].nOffset = AZ_BANK_TO_OFFSET(w);
	m_pMemoryMap[n].nTimingCorrection = ROM_TIMING_CORR_VALUE_SMK;
}


// проверка и отлов кода режима
void CAZBK::AZ_130_In(const uint16_t w)
{
	// отловим nExtCodes
	m_AZData.sAltpro.nExtCodes = w & 014;

	if (!m_bA16M_Trigger && ((w & ALTPRO_BEGIN_STROB_TRIGGER) == ALTPRO_BEGIN_STROB_TRIGGER)) // если строб-код
	{
		m_bA16M_Trigger = true; // взводим триггер, означающий, что любое следующее значение будет кодом режима
		return; // обязательно ретурн, т.к. следующей записью должны принимать код.
	}

	// если никакой не строб, то проверяем
	if (m_bA16M_Trigger) // если триггер взведён
	{
		m_nAltproMode = w & ALTPRO_MODE_MASK;       // вычленим режим
		m_nAltProMemBank = w & ALTPRO_CODE_MASK;    // вычленим код страницы памяти на СМК.

		if ((w & ALTPRO_BEGIN_STROB_TRIGGER) != ALTPRO_BEGIN_STROB_TRIGGER)
		{
			m_AZRegs.nMMUAZ130 = w;
			m_bA16M_Trigger = false;    // сбрасываем триггер
			m_AZData.sAltpro.nAltProMode = m_nAltproMode;
			m_AZData.sAltpro.nAltProMemBank = m_nAltProMemBank;
			AltPro_MemoryManager();    // запускаем менеджер памяти для переключения банков
		}
	}
}

static const A16Mode_t m_SMK512_MemStatus[8] =
{
	// здесь, ПЗУ монитора БК11М будем отключать и в режимах 160, 120, 100
	// потому что на реальном железе оно почему-то не влияет (по крайней мере на реплике с быстрой памятью).
	// и ещё в режиме 100 отключим ОЗУ в окне1, хотя ОЗУ как бы накладывается. Запись туда есть, RPLY выдаётся,
	// но чтение делается только из ОЗУ СМК, ОЗУ БК данные выдать не успевает.
	// Но это сэмулировать не получается пока с моими алгоритмами.

	{
		// режим 0      ALTPRO_SMK_HLT11_MODE
		true, true, false, false, true,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 4, BANK_MODE::RAM }, { 5, BANK_MODE::RAM }, { 6, BANK_MODE::RAM }, { 7, BANK_MODE::RAM }
		}
	},
	{
		// режим 20     ALTPRO_SMK_ALL_MODE
		true, true, true, true, false,
		{
			{ 4, BANK_MODE::RAM }, { 5, BANK_MODE::RAM }, { 6, BANK_MODE::RAM }, { 7, BANK_MODE::RAM }, { 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM },
		}
	},
	{
		// режим 40     ALTPRO_SMK_OZU11_MODE
		false, true, false, false, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 4, BANK_MODE::RAM }, { 5, BANK_MODE::RAM }, { 6, BANK_MODE::RAM }, { 7, BANK_MODE::RAM },
		}
	},
	{
		// режим 60     ALTPRO_SMK_STD10_MODE
		false, true, true, false, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }, { 5, BANK_MODE::RAM }, { 4, BANK_MODE::RAM }, { 0, BANK_MODE::ROM }, { 7, BANK_MODE::RAM },
		}
	},
	{
		// режим 100    ALTPRO_SMK_HLT10_MODE
		true, true /*false*/, true /*false*/, false, true,
		{
			{ 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }, { 4, BANK_MODE::RAM }, { 5, BANK_MODE::RAM }, { 6, BANK_MODE::RAM }, { 7, BANK_MODE::RAM },
		}
	},
	{
		// режим 120    ALTPRO_SMK_OZU10_MODE
		true, true /*false*/, true, false, false,
		{
			{ 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }, { 4, BANK_MODE::RAM }, { 5, BANK_MODE::RAM }, { 6, BANK_MODE::RAM }, { 7, BANK_MODE::RAM },
		}
	},
	{
		// режим 140    ALTPRO_SMK_STD11_MODE
		false, false, false, false, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::ROM }, { 7, BANK_MODE::RAM },
		}
	},
	{
		// режим 160    ALTPRO_SMK_SYS_MODE
		false, true /*false*/, true, true, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 6, BANK_MODE::RAM }, { 7, BANK_MODE::RAM }, { 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 0110, BANK_MODE::ROM }, { 0100, BANK_MODE::ROM },
		}
	},
};

//!!! TODO: ОЗУ по адресу 177000-177777 не эмулируется
void CAZBK::AltPro_MemoryManager()
{
	m_AZData.bAZAltProMM = true;
	// устанавливаем новый режим работы
	const int nSMKMode = (m_AZData.sAltpro.nAltProMode & ALTPRO_MODE_MASK) >> 4;
	const A16Mode_t &ms = m_SMK512_MemStatus[nSMKMode];
	m_AZData.sIOSegment.bReadable = ms.bSMKIOSegR;
	m_AZData.sIOSegment.bWritable = ms.bSMKIOSegW;
	// теперь из массивов выберем нужные значения
	const auto brd = g_Platform.GetBoardPtr();
	brd->Mon10RomOFF(ms.bMON10);
	brd->BOS11RomOFF(ms.bBOS11);
	brd->RAM11Wn1OFF(ms.bWND1);
	int nBaseSegment = 128;
	const int nCode = m_AZData.sAltpro.nAltProMemBank;

	if (nCode & BIT10)
	{
		nBaseSegment += 010;
	}

	if (nCode & BIT02)
	{
		nBaseSegment += 020;
	}

	if (nCode & BIT03)
	{
		nBaseSegment += 040;
	}

	if (nCode & BIT00)
	{
		nBaseSegment += 0100;
	}

	m_AZData.pre_smk_reg_az_win_ctrl = 0;
	m_AZData.pre_smk_reg_az_win_ro = 0;

	for (int n = 0, nBitMask = (1 << 8); n < 8; ++n, nBitMask <<= 1)
	{
		const BankData_t &BData = ms.aBankStatus[n];

		switch (BData.nMode)
		{
			case BANK_MODE::RAM:
				m_AZData.pre_smk_reg_az_win_ctrl |= nBitMask;
				m_AZData.pre_smk_reg_az_mapper[n] = BData.nPage + nBaseSegment;
				break;

			case BANK_MODE::ROM:
				m_AZData.pre_smk_reg_az_win_ctrl |= nBitMask;
				m_AZData.pre_smk_reg_az_mapper[n] = BData.nPage;
				break;
		}
	}

	// и обработаем дополнительные условия
	switch (nSMKMode)
	{
		case (ALTPRO_SMK_HLT10_MODE >> 4):
			m_AZData.pre_smk_reg_az_win_ro |= (1 << 8);
			break;
	}

	// преобразование от SMK
	Set_AZ_Mapper(15, m_AZData.pre_smk_reg_az_mapper[7]); // 170000-177000
	Set_AZ_Mapper(14, m_AZData.pre_smk_reg_az_mapper[6]); // 160000-167777
	Set_AZ_Mapper(13, m_AZData.pre_smk_reg_az_mapper[5]); // 150000-157777
	Set_AZ_Mapper(12, m_AZData.pre_smk_reg_az_mapper[4]); // 140000-147777
	m_AZRegs.nAZ_win_ctrl &= ~0xf000;
	m_AZRegs.nAZ_win_ctrl |= (m_AZData.pre_smk_reg_az_win_ctrl & 0xf000);
	m_AZRegs.nAZ_win_ro &= ~0xf000;
	m_AZRegs.nAZ_win_ro |= (m_AZData.pre_smk_reg_az_win_ro & 0xf000);
	m_AZRegs.nAZ_win_shdw &= ~0xf000;    // принудительно выключаем тень тк вся память SMK это память AZ

	if (ms.bWND1) // есть отключение окна 1 и мы оверайдим из SMK данные
	{
		Set_AZ_Mapper(11, m_AZData.pre_smk_reg_az_mapper[3]); // 130000-137777
		Set_AZ_Mapper(10, m_AZData.pre_smk_reg_az_mapper[2]); // 120000-127777
		Set_AZ_Mapper(9, m_AZData.pre_smk_reg_az_mapper[1]);  // 110000-117777
		Set_AZ_Mapper(8, m_AZData.pre_smk_reg_az_mapper[0]);  // 100000-107777
		m_AZRegs.nAZ_win_ctrl &= ~0x0f00;
		m_AZRegs.nAZ_win_ctrl |= (m_AZData.pre_smk_reg_az_win_ctrl & 0x0f00);
		m_AZRegs.nAZ_win_ro &= ~0x0f00;
		m_AZRegs.nAZ_win_ro |= (m_AZData.pre_smk_reg_az_win_ro & 0x0f00);
		m_AZRegs.nAZ_win_shdw &= ~0x0f00; // принудительно выключаем тень в зоне пересечения с SMK - те на окне 1
		m_AZRegs.nAZ_mapper_ctrl |= AZ_MMU_SMK_WND1_ENA; // отключение окна 1 на БК11М
	}
	else // иначе окно принадлежит мапперу БК11М
	{
		// окно 1 - может отключаться или эмулировать ПЗУ. адреса пишем всегда
		Set_AZ_Mapper(8, m_AZData.pre_b11m_reg_az_mapper[4]);     // 100000-107777
		Set_AZ_Mapper(9, m_AZData.pre_b11m_reg_az_mapper[5]);     // 110000-117777
		Set_AZ_Mapper(10, m_AZData.pre_b11m_reg_az_mapper[6]);   // 120000-127777
		Set_AZ_Mapper(11, m_AZData.pre_b11m_reg_az_mapper[7]);    // 130000-137777
		m_AZRegs.nAZ_win_ctrl &= ~0x0f00;
		m_AZRegs.nAZ_win_ctrl |= m_AZData.pre_b11m_reg_az_win_ctrl;
		m_AZRegs.nAZ_win_ro &= ~0x0f00;    // сбрасываем на всякий случай флаг запрета записи
		m_AZRegs.nAZ_win_shdw &= ~0x0f00;
		m_AZRegs.nAZ_win_shdw |= m_AZData.pre_b11m_reg_az_win_shdw;
		m_AZRegs.nAZ_mapper_ctrl &= ~AZ_MMU_SMK_WND1_ENA; // отключение окна 1 на БК11М
	}

	MemoryManager();
}

