// BKMPIDevice.h: interface for the CBKMPIDevice class.
//

#pragma once


#include "Device.h"
#include "Config.h"
#include "MSFManager.h"

template<typename T>
constexpr auto BANK_TO_OFFSET(T bnk)
{
	return (bnk << 13);
}

template<typename T>
constexpr auto ADDRESS_TO_BANK(T a)
{
	return ((a >> 13) & 07);
}


class CBKMPIDevice : public CDevice
{
	public:
		struct BoardRomList_t
		{
			int nResource;
			int nBank;
		};

	protected:
		uint16_t            m_nBKPortsIOArea;   // Адрес, выше которого нет памяти, и все адреса считаются регистрами и портами
		uint8_t            *m_pMemory;          // Массив памяти устройства. (Если памяти нету, то всё равно резервируется 64кб по умолчанию)
		uint32_t            m_nMemorySize;      // Размер массива памяти (По умолчанию == 64кб)
		BKMEMBank_t         m_MemoryMap[8];     // Карта памяти 64кбайтного адресного пространства, там помещается 8 страниц по 8 кб.
		uint32_t            m_nROMPresent;      // Карта загруженных ПЗУ для конфигурации

		// Инициализация памяти
		virtual bool        OnInitDevice()
		{
			return true;
		};
		virtual bool        InitMemoryModules(const BoardRomList_t arrBoardRomList[], int size);
		virtual bool        InitMemory(uint16_t wHighLim);
		bool                LoadRomModule(int iniRomNameIndex, int offset, int nBank);   // загрузка нужного модуля ПЗУ по заданному адресу
		virtual void        OnLoadRomModule(int nSize, int nBank);
		virtual void        MemoryManager();

	public:
		CBKMPIDevice();
		virtual ~CBKMPIDevice() override;

		uint8_t *GetMainMemory()
		{
			return m_pMemory;
		}

		virtual BK_DEV_MPI  GetBoardModel();
		virtual bool        InitDevice(uint16_t addrStart)
		{
			return OnInitDevice();
		};

		// Virtual method called after reset command
		virtual void        OnReset() override {}

		// Methods for Set/Get byte/word
		virtual bool        GetByteT(register uint16_t addr, register uint8_t *pValue, int &nTC)
		{
			return false;
		}
		virtual bool        GetWordT(register uint16_t addr, register uint16_t *pValue, int &nTC)
		{
			return false;
		}
		virtual bool        SetByteT(register uint16_t addr, register uint8_t value, int &nTC)
		{
			return false;
		}
		virtual bool        SetWordT(register uint16_t addr, register uint16_t value, int &nTC)
		{
			return false;
		}

		// функции без выдачи таймингов, там где тайминги не нужны
		virtual bool        GetByte(register uint16_t addr, register uint8_t *pValue) override
		{
			int nTC = 0;
			return GetByteT(addr, pValue, nTC);
		}
		virtual bool        GetWord(register uint16_t addr, register uint16_t *pValue) override
		{
			int nTC = 0;
			return GetWordT(addr, pValue, nTC);
		}
		virtual bool        SetByte(register uint16_t addr, register uint8_t value) override
		{
			int nTC = 0;
			return SetByteT(addr, value, nTC);
		}
		virtual bool        SetWord(register uint16_t addr, register uint16_t value) override
		{
			int nTC = 0;
			return SetWordT(addr, value, nTC);
		}


		// методы для безопасного чтения/записи в память (нужны далеко не везде,
		// поэтому - по умолчанию - выполняют ту же функцию что и обычные методы)
		virtual bool        GetByteIndirect(register uint16_t addr, register uint8_t *pValue)
		{
			int nTC = 0;
			return GetByteT(addr, pValue, nTC);
		}
		virtual bool        GetWordIndirect(register uint16_t addr, register uint16_t *pValue)
		{
			int nTC = 0;
			return GetWordT(addr, pValue, nTC);
		}
		virtual bool        SetByteIndirect(register uint16_t addr, register uint8_t value)
		{
			int nTC = 0;
			return SetByteT(addr, value, nTC);
		}
		virtual bool        SetWordIndirect(register uint16_t addr, register uint16_t value)
		{
			int nTC = 0;
			return SetWordT(addr, value, nTC);
		}


		// Методы для загрузки и сохранения состояния эмулятора
		virtual bool        RestoreRegisters(DWORD id, CMSFManager *pMsf)
		{
			return true;
		}
		virtual bool        RestoreMemory(DWORD id, CMSFManager *pMsf)
		{
			return true;
		}
		virtual bool        RestoreMemoryMap(DWORD id, CMSFManager *pMsf)
		{
			return true;
		}
		virtual bool        RestoreCustomData(DWORD id, CMSFManager *pMsf)
		{
			return true;
		}
};
