// BKMPIDevice.cpp: implementation of the CBKMPIDevice class.
//


#include "pch.h"
#include "BKMessageBox.h"
#include "BKMPIDevice.h"
#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
// Construction/Destruction

IMPLEMENT_DYNAMIC(CBKMPIDevice, CObject)

CBKMPIDevice::CBKMPIDevice()
    = default;

bool CBKMPIDevice::InitialSetDevice()
{
	m_pMemoryMap.resize(DEC_BANKS_NUM);

	if (m_pMemoryMap.data())
	{
		InitMemory(BK_PORTSIO_AREA);    // Инициализация модуля памяти
		MemoryManager();                // Начальная инициализация
		return true;
	}

	g_BKMsgBox.Show(IDS_BK_ERROR_NOTENMEMR, MB_OK);
	return false;
}


CBKMPIDevice::~CBKMPIDevice()
    = default;

bool CBKMPIDevice::InitMemory(const uint16_t wHighLim)
{
	m_nBKPortsIOArea = wHighLim;
	m_pMemory.clear();

	if (m_nMemorySize)
	{
		m_pMemory.resize(m_nMemorySize);

		if (m_pMemory.data())
		{
			uint16_t val = 0;
			uint8_t flag = 0;
			auto pwMem = reinterpret_cast<uint16_t *>(m_pMemory.data());

			for (uint32_t i = 0; i < m_nMemorySize / 2; ++i, flag--)
			{
				pwMem[i] = val;
				val = ~val;

				if (flag == 192)
				{
					val = ~val;
					flag = 0;
				}
			}

			return true;
		}

		g_BKMsgBox.Show(IDS_BK_ERROR_NOTENMEMR, MB_OK);
		throw;
	}

	return false;
}

bool CBKMPIDevice::InitMemoryModules(const std::span<const BoardRomList_t> arrBoardRomList)
{
	// проверяем загрузку всех ПЗУ в списке.
	// если чего-то нет, выдаём сообщение, но продолжаем
	for (auto &n : arrBoardRomList)
	{
		const int Bnk = n.nBank;

		if (LoadRomModule(n.nResource, BANK_TO_OFFSET(Bnk), Bnk))
		{
			m_nROMPresent |= (1 << Bnk);
		}
		else
		{
			m_nROMPresent &= ~(1 << Bnk);
		}
	}

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

// Загрузка заданного модуля по заданному адресу.
// Вход: iniRomNameIndex - ИД ресурса, обозначающего строковое имя параметра в ини файле
//       offset - смещение в массиве памяти, куда загружать. (смещения вычисляются по разным
//                алгоритмам для АльтПро и остальных, а функция - одна)
//       nBank - номер банка, по которому вычисляется смещение, лишний параметр, но так проще
//               передать его в функцию OnLoadRomModule
// Выход: true - ПЗУ загружено успешно
//       false - ПЗУ не загружено, т.к. не задано или из-за ошибки
bool CBKMPIDevice::LoadRomModule(const int iniRomNameIndex, const int offset, const int nBank)
{
	const std::wstring strName = g_Config.GetRomModuleName(iniRomNameIndex);

	if (Global::isEmptyUnit(strName))
	{
		return false;
	}

	const fs::path strPath = g_Config.m_strROMPath / strName;
	CFile file;

	if (file.Open(strPath.c_str(), CFile::modeRead))
	{
		auto len = static_cast<UINT>(file.GetLength());

		if (len > 020000) // размер ПЗУ не должен быть больше 8кб
		{
			len = 020000;
		}

		const auto pBasePtr = const_cast<uint8_t *>(GetMainMemory()) + offset;
		const UINT readed = file.Read(pBasePtr, len);
		file.Close();

		if (readed < 020000)
		{
			// остаток забиваем нулями
			memset(pBasePtr + readed, 0, 020000ull - static_cast<size_t>(readed));
		}

		if (readed == len)
		{
			OnLoadRomModule(readed, nBank);
			return true;
		}
	}
	else
	{
		const CString strError(MAKEINTRESOURCE(IDS_ERROR_CANTOPENFILE));
		g_BKMsgBox.Show(strError + _T('\'') + CString(strPath.c_str()) + _T('\''), MB_OK | MB_ICONSTOP);
	}

	return false;
}

void CBKMPIDevice::MemoryManager()
{
	for (int i = 0; i < DEC_BANKS_NUM; ++i)
	{
		BKMEMBank_t &m = m_pMemoryMap.at(i);
		m.bReadable = FALSE;
		m.bWritable = FALSE;
		m.nBank = i;
		m.nOffset = BANK_TO_OFFSET(i);
		m.nTimingCorrection = RAM_TIMING_CORR_VALUE_D;
		SetMemoryMapViewData(i);
	}
}

// вход - номер сегмента 0..7
void CBKMPIDevice::SetMemoryMapViewData(const int seg)
{
	const auto &s = g_arStrMMVw1[0];
	g_arStrMMVData[m_arStrMMVIndex][seg] = s;
	int n = seg * 2;
	g_arStrStDsData[n++] = s;
	g_arStrStDsData[n] = s;
}

#if (_DEBUG)
void CBKMPIDevice::DumpMemory(const std::wstring &name)
{
	const fs::path strPathName = g_Config.m_strSavesPath / name;

	if (m_pMemory.data())
	{
		FILE *dump = _tfopen(strPathName.c_str(), _T("wbST"));
		fwrite(m_pMemory.data(), 1, m_nMemorySize, dump);
		fclose(dump);
	}
}
#endif

