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

#pragma once

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

constexpr auto DEC_BANKS_NUM = 8;

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);
}

// флаги операций для функций обращения к системным регистрам
constexpr auto GSSR_NONE	 = 0;
constexpr auto GSSR_BYTEOP	 = 1;
constexpr auto GSSR_INTERNAL = 2;

class CBKMPIDevice : public CObject
{
		DECLARE_DYNAMIC(CBKMPIDevice)

	public:
		struct BoardRomList_t
		{
			int nResource;
			int nBank;
		};

	protected:
		ULONGLONG           m_tickCount; // Device ticks
		CBKMPIDevice       *m_pParent;

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

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

	public:
		CBKMPIDevice();
		virtual ~CBKMPIDevice() override;
		virtual bool        InitialSetDevice();
		virtual CString     GetMemoryMapViewData(const int seg);

		void AttachParent(CBKMPIDevice *pParent)
		{
			m_pParent = pParent;
		};
		// Method for count device ticks
		void                Reset()
		{
			m_tickCount = 0;
			OnReset();
		}

		virtual void NextTick()
		{
			m_tickCount++;
		}

		inline const uint8_t     *GetMainMemory() const
		{
			return m_pMemory.data();
		}

		virtual BK_DEV_MPI  GetBoardModel() const
		{
			return BK_DEV_MPI::NONE;
		}

		virtual CString  GetBoardName() const
		{
			return _T("Прототип");
		}

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

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

		// Методы, эмулирующие поведение регистров
		virtual bool        GetSystemRegister(const uint16_t addr, void *pDst, UINT dwFlags = 0)
		{
			ASSERT(false);
			return false;
		}
		virtual bool        SetSystemRegister(const uint16_t addr, uint16_t value, UINT dwFlags = 0)
		{
			ASSERT(false);
			return false;
		}

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

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


		// методы для безопасного чтения/записи в память (нужны далеко не везде,
		// поэтому - по умолчанию - выполняют ту же функцию что и обычные методы)
		virtual bool        GetByteIndirect(const uint16_t addr, uint8_t *pValue)
		{
			int nTC = 0;
			return GetByteTInternal(addr, pValue, nTC);
		}
		virtual bool        GetWordIndirect(const uint16_t addr, uint16_t *pValue)
		{
			int nTC = 0;
			return GetWordTInternal(addr, pValue, nTC);
		}
		virtual bool        SetByteIndirect(const uint16_t addr, uint8_t value)
		{
			int nTC = 0;
			return SetByteTInternal(addr, value, nTC);
		}
		virtual bool        SetWordIndirect(const uint16_t addr, uint16_t value)
		{
			int nTC = 0;
			return SetWordTInternal(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;
		}

#if (_DEBUG)
		void DumpMemory(const CString name);
#endif

};
