
#include "pch.h"
#include "resource.h"
#include "FDD_A16M.h"

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

IMPLEMENT_DYNAMIC(CFDD_A16M, CFDDStd326)

CFDD_A16M::CFDD_A16M()
	: m_bA16M_Trigger(false)
	, m_nAltproMode(0)
	, m_nAltProMemBank(0)
{
	m_sCustom = { 0, 0, 0160 };
}

CFDD_A16M::~CFDD_A16M()
    = default;

bool CFDD_A16M::InitialSetDevice()
{
	// Инициализация модуля памяти, 16кб ОЗУ + 8кб ПЗУ (из них используется 4).
	m_nMemorySize = 060000;

	if (CBKMPIDevice::InitialSetDevice())
	{
		m_pFloppy[static_cast<int>(FDD_DRIVE::A)] = std::make_unique<CFloppyDrive>();
		m_pFloppy[static_cast<int>(FDD_DRIVE::B)] = std::make_unique<CFloppyDrive>();
		g_Platform.GetWndVectorPtr()->push_back({ _T("A16M:0"), 0100000, 040000, GetMainMemory() });
		return true;
	}

	return false;
}

bool CFDD_A16M::InitDevice(uint16_t addrStart)
{
	m_sCustom.nAltProMemBank = 0; // нужно выполнять инициализацию перед инициализацией памяти
	m_sCustom.nExtCodes = 0;
	m_sCustom.nAltProMode = 0160;
	return CFDDStd326::InitDevice(addrStart);
}


void CFDD_A16M::OnReset()
{
	m_sCustom.nAltProMemBank = 0;   // нужно выполнять инициализацию, если мы просто выполним ресет
	m_sCustom.nExtCodes = 0;        // получается дублирование, но это недостаток алгоритма
	m_sCustom.nAltProMode = 0160;
	CFDDStd326::OnReset();
}


bool CFDD_A16M::InitMemoryModules(const BoardRomList_t arrBoardRomList[], int size)
{
	// проверяем загрузку всех ПЗУ в списке.
	// если чего-то нет, выдаёт сообщение, но продолжаем
	for (int n = 0; n < size; ++n)
	{
		if (LoadRomModule(arrBoardRomList[n].nResource, BANK_TO_OFFSET_ALTPRO(arrBoardRomList[n].nBank), arrBoardRomList[n].nBank))
		{
			m_nROMPresent |= (1 << arrBoardRomList[n].nBank);
		}
		else
		{
			m_nROMPresent &= ~(1 << arrBoardRomList[n].nBank);
		}
	}

	// в любом случае проинициализируем карту памяти
	MemoryManager();
	return true;
}

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

void CFDD_A16M::OnLoadRomModule(const int nSize, const int nBank)
{
	if (nBank == ALTPRO_A16M_FDDROM_BNK)
	{
		if (nSize <= 010000)
		{
			m_nBKPortsIOArea = 0170000;
		}
	}
}

static const A16Mode_t m_A16M_MemStatus[8] =
{
	{
		// режим 0      ALTPRO_A16M_HLT11_MODE
		// откл  MON10   MON11,  RAM10
		true,   true,   false, false, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }
		}
	},
	{
		// режим 20     ALTPRO_A16M_BASIC_MODE
		true,   true,   true, false, false,
		{
			{ 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF },
		}
	},
	{
		// режим 40     ALTPRO_A16M_OZU11_MODE
		false,  true,   false, false, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 2, BANK_MODE::RAM }, { 0, BANK_MODE::OFF },
		}
	},
	{
		// режим 60     ALTPRO_A16M_STD10_MODE
		false,  true,   true, false, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }, { 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 0, BANK_MODE::ROM }, { 0, BANK_MODE::OFF },
		}
	},
	{
		// режим 100    ALTPRO_A16M_OZUZZ_MODE
		true,   false,  false, false, false,
		{
			{ 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::ROM }, { 0, BANK_MODE::OFF },
		}
	},
	{
		// режим 120    ALTPRO_A16M_OZU10_MODE
		true,   false,  true, false, false,
		{
			{ 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 0, BANK_MODE::ROM }, { 0, BANK_MODE::OFF },
		}
	},
	{
		// режим 140    ALTPRO_A16M_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 }, { 0, BANK_MODE::OFF },
		}
	},
	{
		// режим 160    ALTPRO_A16M_START_MODE
		false,  false,  true, false, false,
		{
			{ 0, BANK_MODE::OFF }, { 0, BANK_MODE::OFF }, { 2, BANK_MODE::RAM }, { 3, BANK_MODE::RAM }, { 0, BANK_MODE::RAM }, { 1, BANK_MODE::RAM }, { 0, BANK_MODE::ROM }, { 0, BANK_MODE::ROM },
		}
	},
};


void CFDD_A16M::MemoryManager()
{
	// устанавливаем новый режим работы
	const int nModeNumber = m_sCustom.nAltProMode >> 4;
	const A16Mode_t &AMode = m_A16M_MemStatus[nModeNumber];
	// теперь из массивов выберем нужные значения
	const auto brd = g_Platform.GetBoardPtr();
	brd->Mon10RomOFF(AMode.bMON10);
	brd->BOS11RomOFF(AMode.bBOS11);
	brd->RAM11Wn1OFF(AMode.bWND1);
	SetBank(nModeNumber);

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

		case (ALTPRO_A16M_HLT11_MODE >> 4):
			m_pMemoryMap[7].bReadable = FALSE; // банк 7 с доступом только по записи
			break;
	}
}

// установка страницы ОЗУ в заданном банке
void CFDD_A16M::SetBank(const int nModeNumber)
{
	const A16Mode_t &mt = m_A16M_MemStatus[nModeNumber];

	for (int nBank = 0; nBank < ALTPRO_BANKS_NUM; ++nBank)
	{
		BKMEMBank_t &mb = m_pMemoryMap[nBank];
		const BANK_MODE nMode = mt.aBankStatus[nBank].nMode;

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

			case BANK_MODE::RAM:
				mb.bReadable = TRUE;
				mb.bWritable = TRUE;
				mb.nBank = mt.aBankStatus[nBank].nPage;
				mb.nOffset = BANK_TO_OFFSET_ALTPRO(mb.nBank);
				mb.nTimingCorrection = RAM_TIMING_CORR_VALUE_S;
				break;

			case BANK_MODE::ROM:
				mb.bReadable = !!(m_nROMPresent & (1 << ALTPRO_A16M_FDDROM_BNK));
				mb.bWritable = FALSE;
				mb.nBank = ALTPRO_A16M_FDDROM_BNK;
				mb.nOffset = BANK_TO_OFFSET_ALTPRO(ALTPRO_A16M_FDDROM_BNK);
				mb.nTimingCorrection = ROM_TIMING_CORR_VALUE_SMK;
				break;
		}
	}
}



bool CFDD_A16M::GetSystemRegister(const uint16_t addr, void *pDst, UINT dwFlags)
{
	bool bRet = false;
	uint16_t w = 0;

	switch (addr & 0177776)
	{
		case 0177130:
			if (!(m_sCustom.nExtCodes & 4)) // бит 2 отключает эти порты по чтению
			{
				w = (dwFlags & GSSR_INTERNAL) ? GetStateDebug() : GetState();
				// TRACE(_T("Rd 177130: %06o\n"), v);
				bRet = true;
			}

			break;

		case 0177132:
			if (!(m_sCustom.nExtCodes & 4)) // бит 2 отключает эти порты по чтению
			{
				w = (dwFlags & GSSR_INTERNAL) ? GetDataDebug() : GetData();
				// TRACE(_T("fdr - pos:%06d  %06o\n"), m_drivedata[m_drive].dataptr, v);
				bRet = true;
			}

			break;
	}

	if (dwFlags & GSSR_BYTEOP)
	{
		*(reinterpret_cast<uint8_t *>(pDst)) = ((addr & 1) ? (w >> 8) : w) & 0377;
	}
	else
	{
		*(reinterpret_cast<uint16_t *>(pDst)) = w;
	}

	return bRet;
}

bool CFDD_A16M::SetSystemRegister(const uint16_t addr, uint16_t value, UINT dwFlags)
{
	const uint16_t src = (dwFlags & GSSR_BYTEOP) ? ((addr & 1) ? value << 8 : value & 0377) : value;

	switch (addr & 0177776)
	{
		case 0177130:

			// обрабатываем только при словной операции или байтовой, но с младшим байтом
			if (!((dwFlags & GSSR_BYTEOP) && (addr & 1)))
			{
				SetCommand(src);
				// тут надо добавить манипуляцию дозу
				Change_AltPro_Mode(src);
			}

			return true;

		case 0177132:
			if (!((dwFlags & GSSR_BYTEOP) && (addr & 1)))
			{
				WriteData(src);
			}

			return true;
	}

	return false;
}

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

	if (addr & 0100000)
	{
		const bool bReg = GetSystemRegister(addr, pValue, GSSR_BYTEOP);

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

		// теперь проверим на чтение из банка памяти
		const int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		const bool bMem = !!m_pMemoryMap[nBank].bReadable;

		if (bMem)   // если читать можно, читаем
		{
			nTC = max(nTC, m_pMemoryMap[nBank].nTimingCorrection);
			*pValue |= m_pMemory[static_cast<size_t>(m_pMemoryMap[nBank].nOffset) + (addr & 07777)];
		}

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

	*pValue = 0;
	return false;
}

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

	if (addr & 0100000)
	{
		const bool bReg = GetSystemRegister(addr, pValue, GSSR_NONE);

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

		// теперь проверим на чтение из банка памяти
		const int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		const bool bMem = !!m_pMemoryMap[nBank].bReadable;

		if (bMem)           // если читать можно, читаем
		{
			nTC = max(nTC, m_pMemoryMap[nBank].nTimingCorrection);
			*pValue |= *(uint16_t *)&m_pMemory[static_cast<size_t>(m_pMemoryMap[nBank].nOffset) + (addr & 07776)];
		}

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

	*pValue = 0;
	return false;
}

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

	if (addr & 0100000)
	{
		const bool bReg = SetSystemRegister(addr, value, GSSR_BYTEOP);

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

		const int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		// теперь проверим на запись
		const bool bMem = !!m_pMemoryMap[nBank].bWritable;

		if (bMem)   // если писать можно, пишем
		{
			nTC = max(nTC, m_pMemoryMap[nBank].nTimingCorrection);
			m_pMemory[static_cast<size_t>(m_pMemoryMap[nBank].nOffset) + (addr & 07777)] = value;
		}

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

	return false;
}

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

	if (addr & 0100000)
	{
		const bool bReg = SetSystemRegister(addr, value, GSSR_NONE);

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

		const int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		// теперь проверим на запись
		const bool bMem = !!m_pMemoryMap[nBank].bWritable;

		if (bMem)   // если писать можно, пишем
		{
			nTC = max(nTC, m_pMemoryMap[nBank].nTimingCorrection);
			*(uint16_t *)&m_pMemory[static_cast<size_t>(m_pMemoryMap[nBank].nOffset) + (addr & 07776)] = value;
		}

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

	return false;
}

bool CFDD_A16M::GetByteTInternal(const uint16_t addr, uint8_t *pValue, int &nTC)
{
	nTC = RAM_TIMING_CORR_VALUE_D;

	if (addr & 0100000)
	{
		const bool bReg = GetSystemRegister(addr, pValue, GSSR_INTERNAL | GSSR_BYTEOP);

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

		// теперь проверим на чтение из банка памяти
		const int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		const bool bMem = !!m_pMemoryMap[nBank].bReadable;

		if (bMem)   // если читать можно, читаем
		{
			nTC = max(nTC, m_pMemoryMap[nBank].nTimingCorrection);
			*pValue |= m_pMemory[static_cast<size_t>(m_pMemoryMap[nBank].nOffset) + (addr & 07777)];
		}

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

	*pValue = 0;
	return false;
}

bool CFDD_A16M::GetWordTInternal(const uint16_t addr, uint16_t *pValue, int &nTC)
{
	nTC = RAM_TIMING_CORR_VALUE_D;

	if (addr & 0100000)
	{
		const bool bReg = GetSystemRegister(addr, pValue, GSSR_INTERNAL);

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

		// теперь проверим на чтение из банка памяти
		const int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		const bool bMem = !!m_pMemoryMap[nBank].bReadable;

		if (bMem)           // если читать можно, читаем
		{
			nTC = max(nTC, m_pMemoryMap[nBank].nTimingCorrection);
			*pValue |= *(uint16_t *)&m_pMemory[static_cast<size_t>(m_pMemoryMap[nBank].nOffset) + (addr & 07776)];
		}

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

	*pValue = 0;
	return false;
}


// проверка и отлов кода режима
void CFDD_A16M::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) == 0);
	}

	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_bA16M_Trigger = false;    // сбрасываем триггер
			m_sCustom.nAltProMode = m_nAltproMode;
			m_sCustom.nAltProMemBank = m_nAltProMemBank;
			MemoryManager();    // запускаем менеджер памяти для переключения банков
		}
	}
}

bool CFDD_A16M::RestoreCustomData(DWORD id, CMSFManager *pMsf)
{
	if (pMsf->IsLoad())
	{
		bool bRet = pMsf->GetBlockCustomData(id, reinterpret_cast<uint8_t *>(&m_sCustom), sizeof(AltProCtrlData_t));

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

			MemoryManager();
		}

		return bRet;
	}

	return pMsf->SetBlockCustomData(id, reinterpret_cast<uint8_t *>(&m_sCustom), sizeof(AltProCtrlData_t));
}


