/*************************************************
*           BK Emulator v3.8 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.
*/


/*
Организация массива памяти (делается один большой массив)
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
*/
// Board.h: interface for the CMotherBoard class.
//


#pragma once


#include <afxtempl.h>

#include "Device.h"
#include "CPU.h"
#include "MSFManager.h"
#include "FDDController.h"
#include "Debugger.h"
#include "Screen.h"
#include "BkSound.h"
#include "Speaker.h"
#include "Covox.h"
#include "emu2149.h"
#include "ExceptionHalt.h"
#include "LockVarType.h"
#include "MainFrm.h"
#include "BKMessageBox.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 = 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_lockRunning;  //флаг состояния - процессор работает/стоит
		LockVarType         m_lockBreaked;  //флаг состояния - процессор приостановлен для отладки
		BOOL                m_bBlockStop;   //блокировка кнопки стоп на бк11м, на бк0010 всегда false
		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;   //событие, говорящее, что надо бы прекратить таймировать
		LockVarType         m_lockTimerWork;        //событие, говорящее, что таймер работает


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

		// Методы для загрузки и сохранения состояния эмулятора
		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);

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

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

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


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

		//переменные для эмуляции луча ЭЛТ
		BYTE               *m_pScreenBufferAddress;
		int                 m_nScreenCycleCount; //нужно обнулять при создании нового экземпляра
		int                 m_nScreenLinesCount;
		int                 m_nScreenLineBytesCount;
		int                 m_nScreenStage;
		int                 m_nScreenLinesStage;
		int                 m_nScreenLineCycleCount;
		BOOL                m_bScreenFrame;
		void                Make_One_Screen_Cycle();

		virtual inline int  GetScreenPage()
		{
			return 1;    //получение номера страницы экрана. для БК10 всегда 1
		}

	public:
		CMotherBoard(BOARD_MODEL model = BK_0010);
		virtual ~CMotherBoard();
		void                StopTimerThread();
		BOOL                StartTimerThread();
		inline BOARD_MODEL  GetBoardModel() const
		{
			return m_BoardModel;
		}
		virtual DWORD       GetConfiguration()
		{
			return MSF_CONF_BK1001;
		}
		virtual BOOL        RestoreState(CMSFManager &msf, HBITMAP hScreenshot);

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

		inline WORD         GetPSW() const
		{
			return m_cpu.GetPSW();
		}
		inline void         SetPSW(WORD value)
		{
			m_cpu.SetPSW(value);
		}
		inline WORD         GetRON(int reg) const
		{
			return m_cpu.GetRON(reg);
		}
		inline void         SetRON(int reg, WORD value)
		{
			m_cpu.SetRON(reg, value);
		}
		inline void         SetBlockStop(BOOL status)
		{
			m_bBlockStop = status;
		}
		inline BOOL         GetBlockStop()
		{
			return m_bBlockStop;
		}
		inline WORD         GetPrevPC()
		{
			return m_nPreviousPC;
		}

		void                AttachWindow(CMainFrame *pParent);
		void                AttachScreen(CScreen *pDevice)
		{
			m_pScreen = pDevice;
		}
		void                AttachSound(CBkSound *pSnd)
		{
			m_pSound = pSnd;
		}
		void                AttachSpeaker(CSpeaker *pDevice)
		{
			m_pSpeaker = pDevice;
		}
		void                AttachCovox(CCovox *pDevice)
		{
			m_pCovox = pDevice;
		}
		void                AttachAY8910(CEMU2149 *pDevice)
		{
			m_pAY8910 = pDevice;
		}
		void                AttachDebugger(CDebugger *pDevice)
		{
			m_pDebugger = pDevice;
		}

		inline int          GetMTC()
		{
			return m_nMediaTicksPerFrame;
		}

		void                StopInterrupt();
		void                UnStopInterrupt();
		void                KeyboardInterrupt(WORD interrupt);
		void                ExecuteFrame(BOOL bCount = TRUE);  // Выполняет набор инструкций входящих в один фрейм

		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); //остановка. по умолчанию сбрасывается отладочный приостанов
		inline BOOL         IsCPURun()
		{
			return (BOOL)m_lockRunning.IsLocked();
		}
		void                BreakCPU();
		void                UnbreakCPU(int nGoto);
		inline BOOL         IsCPUBreaked()
		{
			return (BOOL)m_lockBreaked.IsLocked();
		}

		void                AccelerateCPU();
		void                SlowdownCPU();
		void                AccelerateCPUSmall();
		void                SlowdownCPUSmall();
		inline void         NormalizeCPU()
		{
			m_nCPUFreq = m_nBaseCPUFreq;
		}
		BOOL                CanAccelerate()
		{
			return !(m_nCPUFreq >= m_nHighBound);
		}
		BOOL                CanSlowDown()
		{
			return !(m_nCPUFreq <= m_nLowBound);
		}
		inline int			GetLowBound()
		{
			return m_nLowBound;
		}
		inline int			GetHighBound()
		{
			return m_nHighBound;
		}
		inline LONGLONG     GetCPUTick() const
		{
			return m_tickCount;
		}
		inline void         SetCPUBaseFreq(int frq) //установка базовой частоты
		{
			m_nCPUFreq = m_nBaseCPUFreq = frq;
		}
		inline void         SetCPUFreq(int frq) //установка текущей частоты
		{
			m_nCPUFreq = frq;
		}
		inline int          GetCPUFreq() const //выдача текущей частоты
		{
			return m_nCPUFreq;
		}
		inline int          GetCPUSpeed()   //выдача текущей частоты для конфигурации
		{
			return (m_nCPUFreq == m_nBaseCPUFreq) ? 0 : m_nCPUFreq;
		}
		inline void         EnableAskForBreak(BOOL bEnable)
		{
			m_bAskForBreak = bEnable;
		}
		inline BOOL         IsAskForBreak()
		{
			return m_bAskForBreak;
		}
		//Передача данных экрану
		virtual void        ChangePalette() {}
		inline BOOL         IsColorMode()
		{
			return m_pScreen->IsColorMode();
		}
		inline void         SetColorMode(BOOL bColorMode = TRUE)
		{
			m_pScreen->SetColorMode(bColorMode);
		}
		//Приём/передача данных fdd контроллеру
		inline CFDDController *GetFDD()
		{
			return &m_fdd;
		}
		virtual void        SetFDDType(BKFDD_MODEL model, BOOL bInit = TRUE)
		{
			m_fdd.SetFDDType(model);    //не забывать! BOOL bInit удалять нельзя, оно в других местах нужно.
		}
		BKFDD_MODEL        GetFDDType()
		{
			return m_fdd.GetFDDType();
		}
		inline WORD         GetAltProMode()
		{
			return LOWORD(m_ConfBKModel.nAltProMode);
		}
		inline void         SetAltProMode(DWORD w)
		{
			m_ConfBKModel.nAltProMode = w;
			MemoryManager();
		}
		inline WORD         GetAltProCode()
		{
			return LOWORD(m_ConfBKModel.nAltProMemBank);
		}
		inline void         SetAltProCode(DWORD w)
		{
			m_ConfBKModel.nAltProMemBank = w;
			MemoryManager();
		}
		inline WORD         GetAltProExtCode()
		{
			return LOWORD(m_ConfBKModel.nExtCodes);
		}
		//прочие функции
		inline void         Set177716RegMem(WORD w)
		{
			m_reg177716out_mem = w;
			MemoryManager();
		}

		CONFIG_BKMODEL_STRING  GetConfBKModelNumber()
		{
			return static_cast<CONFIG_BKMODEL_STRING>(m_ConfBKModel.nBKModel);
		}
		BKFDD_MODEL        GetConfFDDModel()
		{
			return g_mstrConfigBKModelParameters[m_ConfBKModel.nBKModel].nBKFDDModel;
		}
		void                SetConfBKModelNumber(CONFIG_BKMODEL_STRING n)
		{
			m_ConfBKModel.nBKModel = n;
		}

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

		void                DrawDebugScreen();
};
