// Board.h: interface for the CMotherBoard class.
//

#pragma once

#include <afxtempl.h>

#include "Device.h"
#include "CPU.h"
#include "FDDController.h"
#include "Debugger.h"
#include "Screen.h"
#include "BkSound.h"
#include "Speaker.h"
#include "Covox.h"
#include "emu2149.h"
#include "MSFManager.h"
#include "ExceptionHalt.h"
#include "LockVarType.h"
#include "MainFrm.h"
#include "Config.h"

#define BRD_10_MON10_BNK 8
#define BRD_10_BASIC10_1_BNK 10
#define BRD_10_BASIC10_2_BNK 12
#define BRD_10_REGISTERS_BNK 14

using namespace std;
class CMainFrame;

class CMotherBoard : public CDevice
{
		friend class CCPU;
		friend class CFDDController;

	public:
		struct BKKeyPrs_t
		{
			int nScanCode;	//скан код первой нажатой клавиши из всех нажатых
			bool bProcessed; //скан код обработали, можно отменять
			bool bKeyPressed; //флаг нажатия и удержания любой алфавитно-цифровой клавиши (для отключения автоповтора)
			void clear()
			{
				nScanCode = -1; //-1 - нет нажатых клавиш
				bProcessed = bKeyPressed = false;
			}
		};
		BKKeyPrs_t			m_kprs; //вектор нажатых клавиш. Значение не важно, важен факт наличия чего-либо в векторе.

		//регистры, публичные, т.к. лень на каждый регистр писать функцию, хотя и можно.
		volatile WORD       m_reg177660;
		volatile WORD       m_reg177662in;
		volatile WORD       m_reg177662out;
		volatile WORD       m_reg177664;
		volatile WORD       m_reg177714in;
		volatile WORD       m_reg177714out;
		volatile WORD       m_reg177716in;
		volatile WORD       m_reg177716out_tap;
		volatile WORD       m_reg177716out_mem;
		enum                { ADDRESS_NONE = -1, GO_INTO = -2, GO_OUT = -3 };

	protected:
		enum                { SPEED_STEP_SMALL = 5, SPEED_STEP = 1000 };
		BOARD_MODEL         m_BoardModel;   //тип модели БК 10 или 11 или 11М
		int                 m_nMediaTicksPerFrame; //количество медиатактов во фрейме.
		int                 m_nCPUTicks;    //текущий счётчик тактов процессора
		int                 m_nCPUFreq;     //текущая частота работы.
		double              m_fTickCount; //количество тактов процессора в текущем фрейме
		double              m_fMedia_Mod; //коэффициент пересчёта тактов процессора в медиа такты
		//означает - через сколько тактов процессора надо вставлять 1 медиа такт, т.к. число не целое, то
		//получается приблизительно.
		double              m_fMediaTicks; //счётчик медиа тактов, происходящих за одну инструкцию
		double              m_fMemory_Mod;
		double              m_fMemoryTicks;
		double              m_fFDD_Mod; //норма 15625.0, больше - быстрее вращение, меньше - медленнее
		double              m_fFDDTicks;

		int                 m_nBaseCPUFreq; //базовая частота работы, по которой вычисляется множитель
		int                 m_nLowBound;    //нижняя граница, меньше которой не может быть частота
		int                 m_nHighBound;   //верхняя граница, больше которой не может быть частота
		WORD                m_nBKPortsIOArea;   //адрес, выше которого нет памяти, и все адреса считаются регистрами и портами
		WORD                m_nStartAddr;    //адрес запуска материнской платы

		CFDDController      m_fdd;          // контроллер 1801ВП1-128.
		CCPU                m_cpu;          // Процессор 1801ВМ1
		BYTE               *m_pMemory;      // Основная память БК + дополнительная для контроллеров
		BKMEMBank_t      m_MemoryMap[16]; //карта памяти 64кбайтного адресного пространства, там помещается 16 банков по 4 кб.
		ConfBKModel_t    m_ConfBKModel;

		CBkSound           *m_pSound;       // указатель на модуль звуковой подсистемы
		CSpeaker           *m_pSpeaker;     // указатель на объект пищалка
		CCovox             *m_pCovox;       // указатель на объект ковокс
		CEMU2149           *m_pAY8910;      // указатель на объект сопроцессор Ay8910-3
		CDebugger          *m_pDebugger;    // указатель на отладчик

		CMainFrame         *m_pParent;      // указатель на родительский фрейм, куда всякие сообщения посылаются
		LockVarType         m_lockBreaked;  //флаг состояния - процессор приостановлен для отладки
		LockVarType         m_lockRunning;  //флаг состояния - процессор работает/стоит
		LockVarType         m_lockExecuteFrame;
		bool                m_bAskForBreak; //при чтении/записи из несуществующего адреса не прерываться. (полезно для отладки и не полезно для работы эмулятора)
		//значение задаётся в ини файле

		// Debug section
		WORD                m_nPreviousPC;   //предыдущее значение регистра РС
		int                 m_nGotoAddress; //адрес, на котором будет сделан отладочный останов

		//переменные для медиатактов
		int                 m_nMediaTickCount;  //счётчик медиа тактов (их должно быть не более g_Config.m_nSoundSampleRate/CPU_FRAMES_PER_SECOND в текущем фрейме)
		BYTE               *m_pSoundBuffer;     //звуковой буфер
		int                 m_nSoundBufferLen;  //длина звукового буфера
		int                 m_nBufPos;          //позиция в звуковом буфере

		//поток с точным таймером
		static UINT AFX_CDECL TimerThreadWrapper(LPVOID lpParam);
		UINT                TimerThreadFunc();    //собственно функция
		CWinThread         *m_pTimerThread;
		LockVarType         m_lockKillTimerEvent;   //флаг для остановки потока.

		//переменные для эмуляции луча ЭЛТ
		WORD                m_nVideoAddress; //видео адрес, младшие 6 бит - счётчик строк внутри строки
		bool                m_bHgate;  //флаг отсчёта служебных видеоциклов в строке
		bool                m_bVgate;  //флаг отсчёта служебных строк
		int                 m_nVGateCounter; //дополнительный счётчик служебных строк
		WORD                m_nLineCounter; //счётчик видео строк
		void                Make_One_Screen_Cycle();

		// Инициализация памяти
		virtual bool        InitMemoryModules();
		void                InitMemoryValues(int nMemSize);
		virtual void        MemoryManager();
		bool                LoadRomModule(int iniRomNameIndex, int bank);   //загрузка нужного модуля ПЗУ по заданному адресу

		// Методы для загрузки и сохранения состояния эмулятора
		virtual bool        RestoreRegisters(CMSFManager &msf);
		virtual bool        RestoreMemory(CMSFManager &msf);
		virtual bool        RestoreMemoryMap(CMSFManager &msf);
		virtual bool        RestorePreview(CMSFManager &msf, HBITMAP hScreenshot);
		virtual bool        RestoreConfig(CMSFManager &msf);


		// Методы, эмулирующие поведение регистров
		virtual bool        OnSetSystemRegister(WORD addr, WORD src, bool bByteOperation = false);
		virtual bool        OnGetSystemRegister(WORD addr, void *pDst, bool bByteOperation = false);

		virtual bool        Interception(); // Вызывается после каждой команды, для перехвата функций
		virtual bool        EmulateTapeOperation();

		virtual int         GetScreenPage(); //получение номера страницы экрана

	public:
		CMotherBoard(BOARD_MODEL model = BK_0010);
		virtual ~CMotherBoard() override;

		virtual DWORD       GetConfiguration();
		BOARD_MODEL         GetBoardModel();

		void                AttachWindow(CMainFrame *pParent);
		void                AttachSound(CBkSound *pSnd);
		void                AttachSpeaker(CSpeaker *pDevice);
		void                AttachCovox(CCovox *pDevice);
		void                AttachAY8910(CEMU2149 *pDevice);
		void                AttachDebugger(CDebugger *pDevice);

		int                 GetMTC();

		void                StopTimerThread();
		bool                StartTimerThread();

		virtual bool        RestoreState(CMSFManager &msf, HBITMAP hScreenshot);

		// Виртуальные методы, вызываемые после команды reset
		virtual void        OnReset() override;
		virtual void        NextTick() override;

		// Методы Set/Get byte/word
		virtual BYTE        GetByte(WORD addr);
		virtual WORD        GetWord(WORD addr);
		virtual void        GetByte(WORD addr, BYTE *pValue) override;
		virtual void        GetWord(WORD addr, WORD *pValue) override;
		virtual void        SetByte(WORD addr, BYTE value) override;
		virtual void        SetWord(WORD addr, WORD value) override;

		virtual BYTE        GetByteIndirect(WORD addr);
		virtual WORD        GetWordIndirect(WORD addr);
		virtual void        SetByteIndirect(WORD addr, BYTE value);
		virtual void        SetWordIndirect(WORD addr, WORD value);

		WORD                GetRON(int reg) const;
		void                SetRON(int reg, WORD value);
		inline WORD         GetPSW() const
		{
			return m_cpu.GetPSW();
		}
		inline void         SetPSW(register WORD value)
		{
			m_cpu.SetPSW(value);
		}
		inline bool         GetPSWBit(int pos)
		{
			return m_cpu.GetPSWBit(pos);
		}
		inline void         SetPSWBit(int pos, bool val)
		{
			m_cpu.SetPSWBit(pos, val);
		}

		WORD                GetPrevPC();

		virtual void        StopInterrupt();
		virtual void        UnStopInterrupt();
		void                KeyboardInterrupt(WORD interrupt);

		void                ExecuteFrame();  // Выполняет набор инструкций входящих в один фрейм

		virtual void        ResetDevices();
		bool                ResetHot(WORD addrStart);
		void                ResetCold(WORD addrMask);
		void                RunInto();
		void                RunOver();
		void                RunOut();
		void                RunToAddr(WORD addr);
		void                RunCPU(bool bUnbreak = true); //запуск. по умолчанию сбрасывается отладочный приостанов
		void                StopCPU(bool bUnbreak = true); //остановка. по умолчанию сбрасывается отладочный приостанов
		bool                IsCPURun();
		bool                IsFrameExecuted();
		void                BreakCPU();
		void                UnbreakCPU(int nGoto);
		bool                IsCPUBreaked();

		void                AccelerateCPU();
		void                SlowdownCPU();
		void                AccelerateCPUSmall();
		void                SlowdownCPUSmall();
		void                NormalizeCPU();
		bool                CanAccelerate();
		bool                CanSlowDown();
		int                 GetLowBound();
		int                 GetHighBound();
		LONGLONG            GetCPUTick() const;
		void                SetCPUBaseFreq(int frq); //установка базовой частоты
		void                SetCPUFreq(int frq); //установка текущей частоты
		int                 GetCPUFreq() const; //выдача текущей частоты
		int                 GetCPUSpeed();   //выдача текущей частоты для конфигурации
		void                EnableAskForBreak(bool bEnable);
		bool                IsAskForBreak();
		//Передача данных экрану
		virtual void        ChangePalette() {}

		//Приём/передача данных fdd контроллеру
		CFDDController     *GetFDD();
		virtual void        SetFDDType(BKFDD_MODEL model, bool bInit = true);
		BKFDD_MODEL         GetFDDType();
		WORD                GetAltProMode();
		void                SetAltProMode(WORD w);
		WORD                GetAltProCode();
		void                SetAltProCode(WORD w);
		WORD                GetAltProExtCode();
		//прочие функции
		void                Set177716RegMem(WORD w);

		//функции для карты памяти
		virtual BYTE       *GetMainMemory();
		virtual BYTE       *GetAddMemory();

		void                DrawDebugScreen();
};

/*
Организация массива памяти (делается один большой массив)
32кб: ОЗУ
000000--банк 0 ---страница 0 --- 0
010000--банк 1
020000--банк 2
030000--банк 3
040000--банк 4 ---страница 1 --- 16384
050000--банк 5
060000--банк 6
070000--банк 7
32кб: ПЗУ
100000--банк 8 ---страница 2 --- 32768 - монитор MONIT10.ROM
110000--банк 9
120000--банк 10------------------------- бейсик Basic10_1.rom
130000--банк 11
140000--банк 12---страница 3 --- 49152 - бейсик Basic10_2.rom
150000--банк 13
160000--банк 14------------------------- бейсик и регистры
170000--банк 15
*/
