#include "stdafx.h"
#include "resource.h"
#include "FDD_SMK512.h"

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



// для AltPro, биты. кодирующие номер страницы
constexpr uint16_t BIT10 = 02000;
constexpr uint16_t BIT03 = 00010;
constexpr uint16_t BIT02 = 00004;
constexpr uint16_t BIT00 = 00001;


CFDD_SMK512::CFDD_SMK512()
	: m_IOSegment(BKMEMBank_t())
{
	// Инициализация модуля памяти, 512кб ОЗУ + 8кб ПЗУ (из них используется 4).
	m_nMemorySize = 02020000;
	InitMemory(BK_PORTSIO_AREA);
}

CFDD_SMK512::~CFDD_SMK512()
{}

BK_DEV_MPI CFDD_SMK512::GetBoardModel()
{
	return BK_DEV_MPI::SMK512;
}


void CFDD_SMK512::OnReset()
{
	CFDD_A16M::OnReset();
	InitHDD();
}

bool CFDD_SMK512::OnInitDevice()
{
	static const CBKMPIDevice::BoardRomList_t m_arrFDDSMK512RomList[FDDRomListSize] =
	{
		// инициализируем список загружаемых ПЗУ
		{ IDS_INI_FDR_SMK512, ALTPRO_SMK_FDDROM_BNK }
	};
	// инициализация карты памяти
	return InitMemoryModules(m_arrFDDSMK512RomList, FDDRomListSize);
}

void CFDD_SMK512::OnLoadRomModule(int nSize, int nBank)
{
	if (nBank == ALTPRO_SMK_FDDROM_BNK)
	{
		if (nSize <= 010000)
		{
			m_nBKPortsIOArea = 0170000;
		}
	}
}

static const A16Mode_t m_SMK512_MemStatus[8] =
{
	// здесь, ПЗУ монитора БК11М будем отключать и в режимах 160, 120, 100
	// потому что на реальном железе оно почему-то не влияет (по крайней мере на реплике с быстрой памятью).
	// и ещё в режиме 100 отключим ОЗУ в окне1, хотя ОЗУ как бы накладывается. Запись туда есть, RPLY выдаётся,
	// но чтение делается только из ОЗУ СМК, ОЗУ БК данные выдать не успевает.
	// Но это сэмулировать не получается пока с моими алгоритмами.
	{
		// режим 160
		false, true /*false*/, true,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 6, BANK_MODE::RAM }, { 7, BANK_MODE::RAM }, { 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 0, BANK_MODE::ROM }, { 0, BANK_MODE::ROM },
		}
	},
	{
		// режим 60
		false, true, true,
		{
			{ 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 },
		}
	},
	{
		// режим 120
		true, true /*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 },
		}
	},
	{
		// режим 20
		true, true, true,
		{
			{ 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 },
		}
	},
	{
		// режим 140
		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 },
		}
	},
	{
		// режим 40
		false, true, 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 },
		}
	},
	{
		// режим 100
		true, true /*false*/, true /*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 },
		}
	},
	{
		// режим 0
		true, true, 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 }
		}
	}
};


void CFDD_SMK512::MemoryManager()
{
	// устанавливаем новый режим работы
	register int nModeNumber = 0;
	m_IOSegment.bReadable = FALSE;
	m_IOSegment.bWritable = FALSE;

	// перекодируем код режима в порядковый номер
	switch (m_sCustom.nAltProMode & ALTPRO_MODE_MASK)
	{
		case ALTPRO_SMK_SYS_MODE:
		{
			// Start (БК11М: откл.окно1)
			nModeNumber = 0;
			m_IOSegment.bReadable = TRUE;
			break;
		}

		case ALTPRO_SMK_STD10_MODE:
		{
			// Std10 (БК11М: откл.окно1, откл.мон11)
			nModeNumber = 1;
			break;
		}

		case ALTPRO_SMK_OZU10_MODE:
		{
			// ОЗУ10 (БК11М: откл.окно1; БК10: откл.мон10)
			nModeNumber = 2;
			break;
		}

		case ALTPRO_SMK_ALL_MODE:
		{
			// (БК11М: откл.окно1, откл.мон11; БК10: откл.мон10)
			nModeNumber = 3;
			m_IOSegment.bReadable = TRUE;
			break;
		}

		case ALTPRO_SMK_STD11_MODE:
		{
			// Std11
			nModeNumber = 4;
			break;
		}

		case ALTPRO_SMK_OZU11_MODE:
		{
			// ОЗУ11 (БК11М: откл.мон11)
			nModeNumber = 5;
			break;
		}

		case ALTPRO_SMK_HLT10_MODE:
		{
			// Hlt10 (БК10: откл.мон10)
			nModeNumber = 6;
			m_IOSegment.bWritable = TRUE;
			break;
		}

		case ALTPRO_SMK_HLT11_MODE:
		{
			// Hlt11 (БК11М: откл.мон11; БК10: откл.мон10)
			nModeNumber = 7;
			m_IOSegment.bWritable = TRUE;
			break;
		}

		default:
			ASSERT(FALSE);
	}

	// теперь из массивов выберем нужные значения
	register auto brd = g_Platform.GetBoardPtr();
	brd->Mon10RomOFF(m_SMK512_MemStatus[nModeNumber].bMON10);
	brd->BOS11RomOFF(m_SMK512_MemStatus[nModeNumber].bBOS11);
	brd->RAM11Wn1OFF(m_SMK512_MemStatus[nModeNumber].bWND1);
	register int nBaseSegment = 0;
	register int nCode = m_sCustom.nAltProMemBank & ALTPRO_CODE_MASK;

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

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

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

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

	SetBank(nModeNumber, nBaseSegment);

	// и обработаем дополнительные условия
	switch (nModeNumber)
	{
		case 6:
			m_MemoryMap[0].bWritable = FALSE; // банк 0 с защитой от записи
			break;
	}
}

// установка страницы ОЗУ в заданном банке
void CFDD_SMK512::SetBank(int nModeNumber, int nBaseSegment)
{
	for (int nBank = 0; nBank < 8; ++nBank)
	{
		BANK_MODE nMode = m_SMK512_MemStatus[nModeNumber].aBankStatus[nBank].nMode;

		switch (nMode)
		{
			case BANK_MODE::OFF:
				m_MemoryMap[nBank].bReadable = FALSE;
				m_MemoryMap[nBank].bWritable = FALSE;
				m_MemoryMap[nBank].nBank = 0;
				m_MemoryMap[nBank].nOffset = 0;
				m_MemoryMap[nBank].nTimingCorrection = RAM_TIMING_CORR_VALUE_D;
				break;

			case BANK_MODE::RAM:
				m_MemoryMap[nBank].bReadable = TRUE;
				m_MemoryMap[nBank].bWritable = TRUE;
				m_MemoryMap[nBank].nBank = m_SMK512_MemStatus[nModeNumber].aBankStatus[nBank].nPage + nBaseSegment;
				m_MemoryMap[nBank].nOffset = BANK_TO_OFFSET_ALTPRO(m_MemoryMap[nBank].nBank);
				m_MemoryMap[nBank].nTimingCorrection = RAM_TIMING_CORR_VALUE_SMK;
				break;

			case BANK_MODE::ROM:
				m_MemoryMap[nBank].bReadable = TRUE;
				m_MemoryMap[nBank].bWritable = FALSE;
				m_MemoryMap[nBank].nBank = ALTPRO_SMK_FDDROM_BNK;
				m_MemoryMap[nBank].nOffset = BANK_TO_OFFSET_ALTPRO(ALTPRO_SMK_FDDROM_BNK);
				m_MemoryMap[nBank].nTimingCorrection = ROM_TIMING_CORR_VALUE_SMK;
				break;
		}
	}
}




bool CFDD_SMK512::GetSystemRegister(register uint16_t addr, register void *pDst, register bool bByteOperation)
{
	if (CFDD_A16M::GetSystemRegister(addr, pDst, bByteOperation))
	{
		return true;
	}

	register uint16_t w = 0;
	register bool bReg = ReadHDDRegisters(addr, w);

	if (bByteOperation)
	{
		// register auto pDst_B = reinterpret_cast<uint8_t *>(pDst);
		*(reinterpret_cast<uint8_t *>(pDst)) = ((addr & 1) ? (w >> 8) : w) & 0377;
	}
	else
	{
		// register auto pDst_W = reinterpret_cast<uint16_t *>(pDst);
		*(reinterpret_cast<uint16_t *>(pDst)) = w;
	}

	return bReg;
}

bool CFDD_SMK512::SetSystemRegister(register uint16_t addr, register uint16_t value, register bool bByteOperation)
{
	if (CFDD_A16M::SetSystemRegister(addr, value, bByteOperation))
	{
		return true;
	}

	return WriteHDDRegisters(addr, ~value);
}

bool CFDD_SMK512::GetByteT(register uint16_t addr, register uint8_t *pValue, int &nTC)
{
	nTC = RAM_TIMING_CORR_VALUE_D;

	if (addr & 0100000)
	{
		register bool bReg = GetSystemRegister(addr, pValue, true);

		if (bReg)
		{
			nTC = max(nTC, REG_TIMING_CORR_VALUE);
		}

		register bool bMem;

		// теперь проверим на чтение из банка памяти
		if (addr >= 0177000)
		{
			if (bMem = !!m_IOSegment.bReadable)  // это не сравнение, это присваивание
			{
				// если читать можно, читаем
				nTC = max(nTC, m_MemoryMap[7].nTimingCorrection);
				*pValue |= m_pMemory[m_MemoryMap[7].nOffset + (addr & 07777)];
			}
		}
		else
		{
			register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);

			if (bMem = !!m_MemoryMap[nBank].bReadable)  // это не сравнение, это присваивание
			{
				// если читать можно, читаем
				nTC = max(nTC, m_MemoryMap[nBank].nTimingCorrection);
				*pValue |= m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07777)];
			}
		}

		if (bReg || bMem)
		{
			return true;
		}
	}

	*pValue = 0;
	return false;
}

bool CFDD_SMK512::GetWordT(register uint16_t addr, register uint16_t *pValue, int &nTC)
{
	nTC = RAM_TIMING_CORR_VALUE_D;

	if (addr & 0100000)
	{
		register bool bReg = GetSystemRegister(addr, pValue, false);

		if (bReg)
		{
			nTC = max(nTC, REG_TIMING_CORR_VALUE);
		}

		register bool bMem;

		// теперь проверим на чтение из банка памяти
		if (addr >= 0177000)
		{
			if (bMem = !!m_IOSegment.bReadable)  // это не сравнение, это присваивание
			{
				// если читать можно, читаем
				nTC = max(nTC, m_MemoryMap[7].nTimingCorrection);
				*pValue |= *(uint16_t *)&m_pMemory[m_MemoryMap[7].nOffset + (addr & 07776)];
			}
		}
		else
		{
			register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);

			if (bMem = !!m_MemoryMap[nBank].bReadable) // это не сравнение, это присваивание
			{
				// если читать можно, читаем
				nTC = max(nTC, m_MemoryMap[nBank].nTimingCorrection);
				*pValue |= *(uint16_t *)&m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07776)];
			}
		}

		if (bReg || bMem)
		{
			return true;
		}
	}

	*pValue = 0;
	return false;
}

bool CFDD_SMK512::SetByteT(register uint16_t addr, register uint8_t value, int &nTC)
{
	nTC = RAM_TIMING_CORR_VALUE_D;

	if (addr & 0100000)
	{
		register bool bReg = SetSystemRegister(addr, value, true);

		if (bReg)
		{
			nTC = max(nTC, REG_TIMING_CORR_VALUE);
		}

		register bool bMem;

		// теперь проверим на запись
		if (addr >= 0177000)
		{
			if (bMem = !!m_IOSegment.bWritable) // это не сравнение, это присваивание
			{
				// если писать можно, пишем
				nTC = max(nTC, m_MemoryMap[7].nTimingCorrection);
				m_pMemory[m_MemoryMap[7].nOffset + (addr & 07777)] = value;
			}
		}
		else
		{
			register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);

			if (bMem = !!m_MemoryMap[nBank].bWritable) // это не сравнение, это присваивание
			{
				// если писать можно, пишем
				nTC = max(nTC, m_MemoryMap[nBank].nTimingCorrection);
				m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07777)] = value;
			}
		}

		if (bReg || bMem)
		{
			return true;
		}
	}

	return false;
}

bool CFDD_SMK512::SetWordT(register uint16_t addr, register uint16_t value, int &nTC)
{
	nTC = RAM_TIMING_CORR_VALUE_D;

	if (addr & 0100000)
	{
		register bool bReg = SetSystemRegister(addr, value, false);

		if (bReg)
		{
			nTC = max(nTC, REG_TIMING_CORR_VALUE);
		}

		register bool bMem = false;

		// теперь проверим на запись
		if (addr >= 0177000)
		{
			if (bMem = !!m_IOSegment.bWritable) // это не сравнение, это присваивание
			{
				// если писать можно, пишем
				nTC = max(nTC, m_MemoryMap[7].nTimingCorrection);
				*(uint16_t *)&m_pMemory[m_MemoryMap[7].nOffset + (addr & 07776)] = value;
			}
		}
		else
		{
			register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);

			if (bMem = !!m_MemoryMap[nBank].bWritable) // это не сравнение, это присваивание
			{
				// если писать можно, пишем
				nTC = max(nTC, m_MemoryMap[nBank].nTimingCorrection);
				*(uint16_t *)&m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07776)] = value;
			}
		}

		if (bReg || bMem)
		{
			return true;
		}
	}

	return false;
}

void CFDD_SMK512::InitHDD()
{
	CString strHDDImgName = g_Config.GetHDDriveImgName(0);

	// если в канал Master что-то нужно примонтировать
	if (strHDDImgName.CompareNoCase(g_strEmptyUnit))
	{
		m_ATA_IDE.attach(strHDDImgName, 0, false);
	}

	strHDDImgName = g_Config.GetHDDriveImgName(1);

	// если в канал Slave что-то нужно примонтировать
	if (strHDDImgName.CompareNoCase(g_strEmptyUnit))
	{
		m_ATA_IDE.attach(strHDDImgName, 1, true);
	}

	m_ATA_IDE.reset();
}

bool CFDD_SMK512::WriteHDDRegisters(register uint16_t num, register uint16_t data)
{
	register uint16_t addr = num & 0177776;

	// обработка регистров HDD SMK
	switch (addr)
	{
		case 0177740:
			m_ATA_IDE.write_regs((num & 1) ? HDD_REGISTER::H3F7 : HDD_REGISTER::H1F7, data);
			break;

		case 0177742:
			m_ATA_IDE.write_regs((num & 1) ? HDD_REGISTER::H3F6 : HDD_REGISTER::H1F6, data);
			break;

		case 0177744:
			m_ATA_IDE.write_regs(HDD_REGISTER::H1F5, data);
			break;

		case 0177746:
			m_ATA_IDE.write_regs(HDD_REGISTER::H1F4, data);
			break;

		case 0177750:
			m_ATA_IDE.write_regs(HDD_REGISTER::H1F3, data);
			break;

		case 0177752:
			m_ATA_IDE.write_regs(HDD_REGISTER::H1F2, data);
			break;

		case 0177754:
			m_ATA_IDE.write_regs(HDD_REGISTER::H1F1, data);
			break;

		case 0177756:
			m_ATA_IDE.write_regs(HDD_REGISTER::H1F0, data);
			break;

		default:
			return false;
	}

	return true;
}

bool CFDD_SMK512::ReadHDDRegisters(register uint16_t num, register uint16_t &data)
{
	uint16_t addr = num & 0177776;
	register uint16_t result;

	// обработка регистров HDD SMK
	// оказывается, есть ещё и байтовые обращения
	switch (addr)
	{
		case 0177740:
			result = m_ATA_IDE.read_regs((num & 1) ? HDD_REGISTER::H3F7 : HDD_REGISTER::H1F7);
			break;

		case 0177742:
			result = m_ATA_IDE.read_regs((num & 1) ? HDD_REGISTER::H3F6 : HDD_REGISTER::H1F6);
			break;

		case 0177744:
			result = m_ATA_IDE.read_regs(HDD_REGISTER::H1F5);
			break;

		case 0177746:
			result = m_ATA_IDE.read_regs(HDD_REGISTER::H1F4);
			break;

		case 0177750:
			result = m_ATA_IDE.read_regs(HDD_REGISTER::H1F3);
			break;

		case 0177752:
			result = m_ATA_IDE.read_regs(HDD_REGISTER::H1F2);
			break;

		case 0177754:
			result = m_ATA_IDE.read_regs(HDD_REGISTER::H1F1);
			break;

		case 0177756:
			result = m_ATA_IDE.read_regs(HDD_REGISTER::H1F0);
			break;

		default:
			data = 0;
			return false;
	}

	data = ~result;
	return true;
}

uint16_t CFDD_SMK512::ReadDebugHDDRegisters(int nDrive, HDD_REGISTER num, bool bReadMode)
{
	return m_ATA_IDE.get_debug_regs(nDrive, num, bReadMode);
}

// проверка и отлов кода режима
void CFDD_SMK512::Change_AltPro_Mode(uint16_t w)
{
	// отловим nExtCodes
	m_sCustom.nExtCodes = w & 014;

	// подключение/отключение ПЗУ Бейсика
	// только на СМК с памятью < 256кб и А*
	if (g_Platform.GetBoardPtr() && GetBoardModel() != BK_DEV_MPI::SMK512)
	{
		// если бит 3 установлен, то бейсик включён
		g_Platform.GetBoardPtr()->BasicRomOFF(!(m_sCustom.nExtCodes & 010));
	}

	// ключевое отличие ПЛИС реплики - определение строб кода
	if (!m_bA16M_Trigger && ((w & 0xf) == 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 & 0xf) != ALTPRO_BEGIN_STROB_TRIGGER)
		{
			m_bA16M_Trigger = false;    // сбрасываем триггер
			m_sCustom.nAltProMode = m_nAltproMode;
			m_sCustom.nAltProMemBank = m_nAltProMemBank;
			MemoryManager();    // запускаем менеджер памяти для переключения банков
		}
	}
}


