/*************************************************
*           BK Emulator v3.9 for Windows         *
*************************************************/

/*
File: Board.h
Version: 1.0.0
Written by: Yuriy Kalmykov <kalmykov@stoik.com>
    Copyright (c) 2002-2004 Yuriy Kalmykov

    BK Emulator is a program emulated hardware environment for running
code for BK 0010(01) in different configurations.

    This code may be used in compiled form in any way you desire.
This file or it's parts can't be redistributed without the authors
written consent, but can be modified for your private needs.
    Providing that this notice and the authors name and all copyright
notices remains intact.

    Please, an email me to know how you are using it and where. You can
ask me for any information about this below code or any attendant
knowledge.

    This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability for any damage or loss of business that
this product may cause.
*/


// 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

class CMainFrame;

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

	public:
		//регистры, публичные, т.к. лень на каждый регистр писать функцию, хотя и можно.
		WORD                m_reg177660;
		WORD                m_reg177662in;
		WORD                m_reg177662out;
		WORD                m_reg177664;
		WORD                m_reg177714in;
		WORD                m_reg177714out;
		WORD                m_reg177716in;
		WORD                m_reg177716out_tap;
		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_Type      m_MemoryMap[16]; //карта памяти 64кбайтного адресного пространства, там помещается 16 банков по 4 кб.
		ConfBKModel_Type    m_ConfBKModel;

		CScreen            *m_pScreen;      // указатель на модуль подсистемы вывода на экран
		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();
		virtual void        InitMemoryValues();
		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        OnSetSystemRegisterIndirect(WORD addr, WORD src, bool bByteOperation = false);
//      virtual bool        OnGetSystemRegisterIndirect(WORD addr, void *pDst, bool bByteOperation = false);

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

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

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

		virtual DWORD       GetConfiguration();
		BOARD_MODEL         GetBoardModel();

		void                AttachWindow(CMainFrame *pParent);
		void                AttachScreen(CScreen *pDevice);
		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);
		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
*/
