
// MainFrm.cpp : реализация класса CMainFrame
// этот файл стал неприлично большого размера. надо что-то делать.

#include "stdafx.h"
#include "BK.h"
#include "ToolManager.h"
#include "MainFrm.h"
#include "Board.h"
#include "Board_MSTD.h"
#include "Board_EXT32.h"
#include "Board_10_FDD.h"
#include "Board_11.h"
#include "Board_11_FDD.h"
#include "Board_11M.h"
#include "Board_11M_FDD.h"

#include "Screen.h"
#include "MSFManager.h"
#include "LoadImgDlg.h"
#include "LoadMemoryDlg.h"
#include "LoadTapeDlg.h"
#include "TapeManagerDlg.h"
#include "BKSettingsDlg.h"
#include "BKMEM_Dlg.h"
#include "BKErrMsgSys.h"
#include "SliderButton.h"
#include "MSF.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWndEx)

const int  iMaxUserToolbars = 10;
const UINT uiFirstUserToolBarId = AFX_IDW_CONTROLBAR_FIRST + 40;
const UINT uiLastUserToolBarId = uiFirstUserToolBarId + iMaxUserToolbars - 1;

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
	ON_WM_CREATE()
	ON_COMMAND(ID_VIEW_CUSTOMIZE, &OnViewCustomize)
	ON_REGISTERED_MESSAGE(AFX_WM_CREATETOOLBAR, &OnToolbarCreateNew)
	ON_REGISTERED_MESSAGE(AFX_WM_RESETTOOLBAR, &OnToolbarReset)
	ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &OnApplicationLook)
	ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &OnUpdateApplicationLook)

	ON_WM_SETTINGCHANGE()
	ON_WM_PALETTECHANGED()
	ON_WM_COPYDATA()
	ON_WM_CLOSE()
	ON_WM_TIMER()

	ON_MESSAGE(WM_TAPE_FILE_LOAD, &OnFinishEmulateTape)
	ON_MESSAGE(WM_CPU_DEBUGBREAK, &OnCpuBreak)
	ON_MESSAGE(WM_MEMMAP_CLOSE, &OnMemMapClose)
	ON_MESSAGE(WM_MEMDUMP_NEED_UPDATE, &OnMemDumpUpdate)
	ON_MESSAGE(WM_DROP_TARGET, &OnDropFile)
	ON_MESSAGE(WM_SCREENSIZE_CHANGED, &OnScreenSizeChanged)
	ON_MESSAGE(WM_OUTKBDSTATUS, &OnOutKeyboardStatus)
	ON_MESSAGE(WM_START_PLATFORM, &OnStartPlatform)

	ON_MESSAGE(WM_DRAW_DEBUGSCREEN, &OnDrawDebugScreen)
	ON_MESSAGE(WM_DRAW_BKSCREEN, &OnDrawBKScreen)
	ON_MESSAGE(WM_DRAW_BKOSCILL, &OnDrawBKOscill)


	//////////////Команды////////////////////
	ON_NOTIFY(NM_CUSTOMDRAW, ID_OPTIONS_SOUND_VOLUME, &OnLVolumeSlider) // Momental Change volume
	//файл
	ON_COMMAND(ID_FILE_LOADSTATE, &OnFileLoadstate)
	ON_COMMAND(ID_FILE_SAVESTATE, &OnFileSavestate)
	ON_COMMAND(ID_FILE_LOADTAPE, &OnFileLoadtape)
	ON_COMMAND(ID_FILE_SCREENSHOT, &OnFileScreenshot)
	ON_COMMAND_RANGE(ID_FILE_LOADDRIVE_A, ID_FILE_LOADDRIVE_D, &OnFileLoadDrive)
	ON_COMMAND_RANGE(ID_FILE_UMOUNT_A, ID_FILE_UMOUNT_D, &OnFileUnmount)
	//Конфигурация
	ON_COMMAND(ID_CPU_RESETCPU, &OnCpuResetCpu)
	ON_COMMAND(ID_CPU_SURESETCPU, &OnCpuSuResetCpu)
	ON_COMMAND(ID_CPU_LONGRESET, &OnCpuLongReset)
	ON_COMMAND(ID_CPU_RUNBK001001, &OnCpuRunbk001001)
	ON_COMMAND(ID_CPU_RUNBK001001_FOCAL, &OnCpuRunbk001001Focal)
	ON_COMMAND(ID_CPU_RUNBK001001_32K, &OnCpuRunbk00100132k)
	ON_COMMAND(ID_CPU_RUNBK001001_FDD, &OnCpuRunbk001001Fdd)
	ON_COMMAND(ID_CPU_RUNBK001001_FDD16K, &OnCpuRunbk001001Fdd16k)
	ON_COMMAND(ID_CPU_RUNBK001001_FDD_SMK512, &OnCpuRunbk001001FddSmk512)
	ON_COMMAND(ID_CPU_RUNBK001001_FDD_SAMARA, &OnCpuRunbk001001FddSamara)
	ON_COMMAND(ID_CPU_RUNBK0011M, &OnCpuRunbk0011m)
	ON_COMMAND(ID_CPU_RUNBK0011M_FDD, &OnCpuRunbk0011mFDD)
	ON_COMMAND(ID_CPU_RUNBK0011M_FDD_A16M, &OnCpuRunbk0011mFddA16m)
	ON_COMMAND(ID_CPU_RUNBK0011M_FDD_SMK512, &OnCpuRunbk0011mFddSmk512)
	ON_COMMAND(ID_CPU_RUNBK0011M_FDD_SAMARA, &OnCpuRunbk0011mFddSamara)
	ON_COMMAND(ID_CPU_RUNBK0011, &OnCpuRunbk0011)
	ON_COMMAND(ID_CPU_RUNBK0011_FDD, &OnCpuRunbk0011Fdd)
	ON_COMMAND(ID_CPU_RUNBK0011_FDD_A16M, &OnCpuRunbk0011FddA16m)
	ON_COMMAND(ID_CPU_RUNBK0011_FDD_SMK512, &OnCpuRunbk0011FddSmk512)
	ON_COMMAND(ID_CPU_RUNBK0011_FDD_SAMARA, &OnCpuRunbk0011FddSamara)
	ON_COMMAND(ID_CPU_ACCELERATE, &OnCpuAccelerate)
	ON_COMMAND(ID_CPU_SLOWDOWN, &OnCpuSlowdown)
	ON_COMMAND(ID_CPU_NORMALSPEED, &OnCpuNormalspeed)
	//Опции
	ON_COMMAND(ID_OPTIONS_ENABLE_SPEAKER, &OnOptionsEnableSpeaker)
	ON_COMMAND(ID_OPTIONS_ENABLE_COVOX, &OnOptionsEnableCovox)
	ON_COMMAND(ID_OPTIONS_STEREO_COVOX, &OnOptionsStereoCovox)
	ON_COMMAND(ID_OPTIONS_ENABLE_AY8910, &OnOptionsEnableAy8910)
	ON_COMMAND(ID_OPTIONS_LOG_AY8910, &OnOptionsLogAy8910)
	ON_COMMAND(ID_OPTIONS_SPEAKER_FILTHER, &OnOptionsSpeakerFilter)
	ON_COMMAND(ID_OPTIONS_COVOX_FILTHER, &OnOptionsCovoxFilter)
	ON_COMMAND(ID_OPTIONS_AY8910_FILTHER, &OnOptionsAy8910Filter)
	ON_COMMAND(ID_OPTIONS_EMULATE_BKKEYBOARD, &OnOptionsEmulateBkkeyboard)
	ON_COMMAND(ID_OPTIONS_ENABLE_JOYSTICK, &OnOptionsEnableJoystick)
	ON_COMMAND(ID_OPTIONS_USE_SAVESDIRECTORY, &OnOptionsUseSavesdirectory)
	ON_COMMAND(ID_OPTIONS_EMULATE_TAPE_LOADING, &OnOptionsEmulateTapeLoading)
	ON_COMMAND(ID_OPTIONS_EMULATE_TAPE_SAVING, &OnOptionsEmulateTapeSaving)
	ON_COMMAND(ID_OPTIONS_SHOWPERFORMANCEONSTATUSBAR, &OnOptionsShowPerformanceOnStatusbar)
	ON_COMMAND(ID_OPTIONS_EMULATE_FDDIO, &OnOptionsEmulateFddio)
	ON_COMMAND(ID_OPTIONS_TAPEMANAGER, &OnOptionsTapemanager)
	ON_COMMAND(ID_APP_SETTINGS, &OnAppSettings)
	//Отладка
	ON_COMMAND(ID_DEBUG_STARTBREAK, &OnDebugBreak)
	ON_COMMAND(ID_DEBUG_STEPINTO, &OnDebugStepinto)
	ON_COMMAND(ID_DEBUG_STEPOVER, &OnDebugStepover)
	ON_COMMAND(ID_DEBUG_STEPOUT, &OnDebugStepout)
	ON_COMMAND(ID_DEBUG_RUNTOCURSOR, &OnDebugRuntocursor)
	ON_COMMAND(ID_DEBUG_BREAKPOINT, &OnDebugBreakpoint)
	ON_COMMAND(ID_DEBUG_DIALOG_ASK_FOR_BREAK, &OnDebugDialogAskForBreak)
	ON_COMMAND(ID_DEBUG_PAUSE_CPU_AFTER_START, &OnDebugPauseCpuAfterStart)
	ON_COMMAND_RANGE(ID_DEBUG_DUMPREGS_INTERVAL_0, ID_DEBUG_DUMPREGS_INTERVAL_50, &OnDebugDumpregsInterval)
	ON_COMMAND(ID_DEBUG_MEMMAP, &OnDebugMemmap)
	ON_COMMAND(ID_DEBUG_ENABLE_ICLBLOCK, &OnDebugEnableIclblock)
	//Вид
	ON_COMMAND_RANGE(ID_VKBDTYPE_KEYS, ID_VKBDTYPE_MEMBRANE, &OnVkbdtypeKeys)
	ON_COMMAND(ID_VIEW_COLORMODE, &OnViewColormode)
	ON_COMMAND(ID_VIEW_FULLSCREENMODE, &OnViewFullscreenmode)
	ON_COMMAND(ID_VIEW_ADAPTIVEBWMODE, &OnViewAdaptivebwmode)
	ON_COMMAND(ID_VIEW_LUMINOFOREMODE, &OnViewLuminoforemode)
	//Инструменты
	ON_COMMAND_RANGE(ID_TOOL_MENU_0, ID_TOOL_MENU_9, &OnToolLaunch)
	//////////////Интерфейс меню////////////////////
	//файл
	ON_UPDATE_COMMAND_UI(ID_FILE_LOADTAPE, &OnUpdateFileLoadtape)
	ON_UPDATE_COMMAND_UI_RANGE(ID_FILE_LOADDRIVE_A, ID_FILE_LOADDRIVE_D, &OnUpdateFileLoadDrive)
	//Конфигурация
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001, &OnUpdateCpuRunbk001001)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FOCAL, &OnUpdateCpuRunbk001001Focal)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_32K, &OnUpdateCpuRunbk00100132k)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FDD, &OnUpdateCpuRunbk001001Fdd)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FDD16K, &OnUpdateCpuRunbk001001Fdd16k)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FDD_SMK512, &OnUpdateCpuRunbk001001FddSmk512)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FDD_SAMARA, &OnUpdateCpuRunbk001001FddSamara)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M, &OnUpdateCpuRunbk0011m)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M_FDD, &OnUpdateCpuRunbk0011mFDD)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M_FDD_A16M, &OnUpdateCpuRunbk0011mFddA16m)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M_FDD_SMK512, &OnUpdateCpuRunbk0011mFddSmk512)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M_FDD_SAMARA, &OnUpdateCpuRunbk0011mFddSamara)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011, &OnUpdateCpuRunbk0011)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011_FDD, &OnUpdateCpuRunbk0011Fdd)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011_FDD_A16M, &OnUpdateCpuRunbk0011FddA16m)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011_FDD_SMK512, &OnUpdateCpuRunbk0011FddSmk512)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011_FDD_SAMARA, &OnUpdateCpuRunbk0011FddSamara)
	ON_UPDATE_COMMAND_UI(ID_CPU_ACCELERATE, &OnUpdateCpuAccelerate)
	ON_UPDATE_COMMAND_UI(ID_CPU_SLOWDOWN, &OnUpdateCpuSlowdown)

	//Опции
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_SPEAKER, &OnUpdateOptionsEnableSpeaker)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_COVOX, &OnUpdateOptionsEnableCovox)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_STEREO_COVOX, &OnUpdateOptionsStereoCovox)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_AY8910, &OnUpdateOptionsEnableAy8910)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_LOG_AY8910, &OnUpdateOptionsLogAy8910)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_SPEAKER_FILTHER, &OnUpdateOptionsSpeakerFilter)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_COVOX_FILTHER, &OnUpdateOptionsCovoxFilter)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_AY8910_FILTHER, &OnUpdateOptionsAy8910Filter)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_BKKEYBOARD, &OnUpdateOptionsEmulateBkkeyboard)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_JOYSTICK, &OnUpdateOptionsEnableJoystick)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_USE_SAVESDIRECTORY, &OnUpdateOptionsUseSavesdirectory)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_TAPE_LOADING, &OnUpdateOptionsEmulateTapeLoading)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_TAPE_SAVING, &OnUpdateOptionsEmulateTapeSaving)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWPERFORMANCEONSTATUSBAR, &OnUpdateOptionsShowPerformanceOnStatusbar)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_FDDIO, &OnUpdateOptionsEmulateFddio)
	//Отладка
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STARTBREAK, &OnUpdateDebugBreak)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPINTO, &OnUpdateDebugStepinto)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPOVER, &OnUpdateDebugStepover)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPOUT, &OnUpdateDebugStepout)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_RUNTOCURSOR, &OnUpdateDebugRuntocursor)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_DIALOG_ASK_FOR_BREAK, &OnUpdateDebugDialogAskForBreak)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_PAUSE_CPU_AFTER_START, &OnUpdateDebugPauseCpuAfterStart)
	ON_UPDATE_COMMAND_UI_RANGE(ID_DEBUG_DUMPREGS_INTERVAL_0, ID_DEBUG_DUMPREGS_INTERVAL_50, &OnUpdateDebugDumpregsInterval)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_ENABLE_ICLBLOCK, &OnUpdateDebugEnableIclblock)
	//Вид
	ON_UPDATE_COMMAND_UI_RANGE(ID_VKBDTYPE_KEYS, ID_VKBDTYPE_MEMBRANE, &OnUpdateVkbdtypeKeys)
	ON_UPDATE_COMMAND_UI(ID_VIEW_COLORMODE, &OnUpdateViewColormode)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ADAPTIVEBWMODE, &OnUpdateViewAdaptivebwmode)
	ON_UPDATE_COMMAND_UI(ID_VIEW_LUMINOFOREMODE, &OnUpdateViewLuminoforemode)

	ON_COMMAND_RANGE(ID_VIEW_SCREENSIZE_CUSTOM, ID_VIEW_SCREENSIZE_1024X768, &OnSetScreenSize)
	ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_SCREENSIZE_CUSTOM, ID_VIEW_SCREENSIZE_1024X768, &OnUpdateSetScreenSize)

END_MESSAGE_MAP()

static UINT indicators[] =
{
	ID_SEPARATOR,           // индикатор строки состояния
	ID_SEPARATOR,           //тут выводится ЛАТ/РУС
	ID_SEPARATOR,           //тут выводится ЗАГЛ/СТР
	ID_SEPARATOR,           //тут выводится АР2
	ID_SEPARATOR,           //тут выводится СУ
	ID_SEPARATOR,           //тут выводится всякая общая инфа
	ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
};

// создание/уничтожение CMainFrame

CMainFrame::CMainFrame()
	: m_mtInstance(false, _T("BK Emulator Instance"))
	, m_dropTarget()
	, m_pBKMemView(nullptr)
	, m_pChip(nullptr)
	, m_pSound(nullptr)
	, m_pDebugger(nullptr)
	, m_pScreen(nullptr)
	, m_rectMemMap(CRect(0, 0, 0, 0))
	, m_nRegsDumpCounter(0)
	, m_bLongResetPress(false)
	, m_bBeginPeriod(false)
	, m_nScreen_X(0)
	, m_nScreen_Y(0)
	, m_nScreen_CustomX(0)
	, m_nScreen_CustomY(0)
	, m_bBKMemViewOpen(false)
{
	theApp.m_nAppLook = theApp.GetInt(_T("ApplicationLook"), ID_VIEW_APPLOOK_VS_2008);
	m_bBeginPeriod = (timeBeginPeriod(1) == TIMERR_NOERROR);
	m_nStartTick = m_nTickCounter = GetTickCount();
	//регистрируем наше глобальное сообщение
	m_nInterAppGlobalMsg = RegisterWindowMessage(_T("Inter BKEmu Application Global Message"));
	// Инициализация звуковой подсистемы
	m_pSound = new CBkSound();  //нужно обязательно перед установкой текущей громкости

	if (!m_pSound)
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}
}

CMainFrame::~CMainFrame()
{
	if (m_bBeginPeriod)
	{
		timeEndPeriod(1);
	}

	m_wndToolBarSound.CleanUpImages();
	m_wndToolBarDebug.CleanUpImages();
	SAFE_DELETE(m_pChip);
	SAFE_DELETE(m_pSound);
	m_mtInstance.Unlock();
}

#ifdef BIG_BUTTONS
#define IMGCOLL_MENU_HICOLOR IDR_MENU_IMAGES_256_2
#define IMGCOLL_MENU_LOCOLOR IDR_MENU_IMAGES_2
#define MAIN_MENU_HICOLOR IDR_MAINFRAME_256_2
#define MAIN_MENU_LOCOLOR IDR_MAINFRAME_2
#define SOUND_MENU_HICOLOR IDR_SOUNDTOOLBAR_256_2
#define SOUND_MENU_LOCOLOR IDR_SOUNDTOOLBAR_2
#define DEBUG_MENU_HICOLOR IDR_DEBUGTOOLBAR_256_2
#define DEBUG_MENU_LOCOLOR IDR_DEBUGTOOLBAR_2
#else
#define IMGCOLL_MENU_HICOLOR IDR_MENU_IMAGES_256
#define IMGCOLL_MENU_LOCOLOR IDR_MENU_IMAGES
#define MAIN_MENU_HICOLOR IDR_MAINFRAME_256
#define MAIN_MENU_LOCOLOR IDR_MAINFRAME
#define SOUND_MENU_HICOLOR IDR_SOUNDTOOLBAR_256
#define SOUND_MENU_LOCOLOR IDR_SOUNDTOOLBAR
#define DEBUG_MENU_HICOLOR IDR_DEBUGTOOLBAR_256
#define DEBUG_MENU_LOCOLOR IDR_DEBUGTOOLBAR
#endif

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CFrameWndEx::OnCreate(lpCreateStruct) == -1)
	{
		return -1;
	}

	//подменим заголовок окна, добавим к нему версию
	m_strTitle = theApp.m_strProgramTitleVersion;
	BOOL bNameValid;

	if (!m_wndMenuBar.Create(this))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFMENUBARERROR], MB_OK);
		TRACE0("Не удалось создать строку меню\n");
		return -1;      // не удалось создать
	}

	// включить быструю (Alt+перетаскивание) настройку панелей инструментов
	CMFCToolBar::EnableQuickCustomization();
//  if (CMFCToolBar::GetUserImages() == nullptr)
//  {
//      // загрузить изображения пользовательских панелей инструментов
//      if (m_UserImages.Load(_T(".\\UserImages.bmp")))
//      {
//          CMFCToolBar::SetUserImages(&m_UserImages);
//      }
//  }
	/*
	IDR_MENU_IMAGES_256 - ресурс тулбара с картинками, который добавляется в коллекцию картинок, для того
	чтобы можно было по ID картинок данного тулбара менять картинки на кнопках видимых тулбаров.
	сам CMFCToolBar на основе данного тулбара не создаётся, У каждой картинки должен быть свой, уникальный ID
	*/
	CMFCToolBar::AddToolBarForImageCollection(theApp.m_bHiColorIcons ? IMGCOLL_MENU_HICOLOR : IMGCOLL_MENU_LOCOLOR);
	m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);
	FillTools(&m_wndMenuBar);
	// предотвращение фокусировки строки меню на активации
	CMFCPopupMenu::SetForceMenuFocus(FALSE);

	if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
	        !m_wndToolBar.LoadToolBar(theApp.m_bHiColorIcons ? MAIN_MENU_HICOLOR : MAIN_MENU_LOCOLOR))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFSTDTOOLBARERROR], MB_OK);
		TRACE0("Не удалось создать стандартную панель инструментов\n");
		return -1;      // не удалось создать
	}

	CString strToolBarName;
	bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_STANDARD);
	ASSERT(bNameValid);
	m_wndToolBar.SetWindowText(strToolBarName);
	CString strCustomize;
	bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);
	ASSERT(bNameValid);
	m_wndToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);

	if (!m_wndToolBarSound.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC,
	                                CRect(1, 1, 1, 1), IDW_SOUND_TOOLBAR) ||
	        !m_wndToolBarSound.LoadToolBar(theApp.m_bHiColorIcons ? SOUND_MENU_HICOLOR : SOUND_MENU_LOCOLOR))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFSNDTOOLBARERROR], MB_OK);
		TRACE0("Не удалось создать звуковую панель инструментов\n");
		return -1;      // не удалось создать
	}

	bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_SOUND);
	ASSERT(bNameValid);
	m_wndToolBarSound.SetWindowText(strToolBarName);
	m_wndToolBarSound.EnableCustomizeButton(true, ID_VIEW_CUSTOMIZE, strCustomize);

	if (!m_wndToolBarDebug.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC,
	                                CRect(1, 1, 1, 1), IDW_DEBUG_TOOLBAR) ||
	        !m_wndToolBarDebug.LoadToolBar(theApp.m_bHiColorIcons ? DEBUG_MENU_HICOLOR : DEBUG_MENU_LOCOLOR))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDBGTOOLBARERROR], MB_OK);
		TRACE0("Не удалось создать панель инструментов отладки\n");
		return -1;      // не удалось создать
	}

	bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_DEBUG);
	ASSERT(bNameValid);
	m_wndToolBarDebug.SetWindowText(strToolBarName);
	m_wndToolBarDebug.EnableCustomizeButton(true, ID_VIEW_CUSTOMIZE, strCustomize);
	// Разрешить операции с пользовательскими панелями инструментов:
	InitUserToolbars(nullptr, uiFirstUserToolBarId, uiLastUserToolBarId);
	// Настройки всех тулбаров: высота
	UpdateToolbarSize();

	if (!m_wndStatusBar.Create(this))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFSTATUSBARERROR], MB_OK);
		TRACE0("Не удалось создать строку состояния\n");
		return -1;      // не удалось создать
	}

	m_wndStatusBar.SetIndicators(indicators, sizeof(indicators) / sizeof(UINT));
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_KBD_XLAT, 25);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_KBD_CAPS, 35);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_KBD_AR2, 25);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_KBD_SU, 25);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_INFO, 352);
	m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
	m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
	m_wndToolBarSound.EnableDocking(CBRS_ALIGN_ANY);
	m_wndToolBarDebug.EnableDocking(CBRS_ALIGN_ANY);
	EnableDocking(CBRS_ALIGN_ANY);
	DockPane(&m_wndMenuBar);
	DockPane(&m_wndToolBar);
	DockPane(&m_wndToolBarSound);
	DockPane(&m_wndToolBarDebug);
	// включить режим работы закрепляемых окон стилей Visual Studio 2005
	CDockingManager::SetDockingMode(DT_SMART);
	// включить режим работы автоматического скрытия закрепляемых окон стилей Visual Studio 2005
	EnableAutoHidePanes(CBRS_ALIGN_ANY);

	// создать закрепляемые окна
	if (!CreateDockingWindows())
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKINGBARERROR], MB_OK);
		TRACE0("Не удалось создать закрепляемые окна\n");
		return -1;
	}

	m_paneDisassembleView.EnableDocking(CBRS_ALIGN_ANY);
	m_paneMemoryDumpView.EnableDocking(CBRS_ALIGN_ANY);
	m_paneBKVKBDView.EnableDocking(CBRS_ALIGN_ANY);
	m_paneOscillatorView.EnableDocking(CBRS_ALIGN_ANY);
	m_paneRegistryDumpViewCPU.EnableDocking(CBRS_ALIGN_ANY);
	m_paneRegistryDumpViewFDD.EnableDocking(CBRS_ALIGN_ANY);
	m_paneTapeCtrlView.EnableDocking(CBRS_ALIGN_ANY);
	DockPane(&m_paneDisassembleView);
	m_paneMemoryDumpView.DockToWindow(&m_paneDisassembleView, CBRS_ALIGN_BOTTOM); //для этого панели m_DisassembleView и m_memoryDumpView обязательно должны быть видимые.
	//иначе, m_memoryDumpView не может придокиться к m_DisassembleView и остаётся висеть в воздухе, так что даже перемещать её нельзя.
	DockPane(&m_paneBKVKBDView);
	DockPane(&m_paneOscillatorView);
	m_paneBKVKBDView.AttachToTabWnd(&m_paneOscillatorView, DM_SHOW, false, nullptr);
	DockPane(&m_paneRegistryDumpViewCPU);
	DockPane(&m_paneRegistryDumpViewFDD);
	m_paneTapeCtrlView.DockToWindow(&m_paneRegistryDumpViewCPU, CBRS_ALIGN_BOTTOM);
	m_paneRegistryDumpViewFDD.AttachToTabWnd(&m_paneMemoryDumpView, DM_SHOW, false, nullptr);
	// установите наглядный диспетчер и стиль на основе постоянного значения
	OnApplicationLook(theApp.m_nAppLook);
	m_paneRegistryDumpViewCPU.AttachDebugger(m_pDebugger);
	m_paneRegistryDumpViewFDD.AttachDebugger(m_pDebugger);
	m_paneMemoryDumpView.AttachDebugger(m_pDebugger);
	m_paneDisassembleView.AttachDebugger(m_pDebugger);
	m_dropTarget.Register(this);
	// Включить функцию замены меню панелей инструментов и закрепляемых окон
	EnablePaneMenu(TRUE, ID_VIEW_CUSTOMIZE, strCustomize, ID_VIEW_TOOLBAR);
	InitRegistry(); //регистрируем типы и расширения, чтоб по клику на файлах прога запускалась
	CString strLine = GetCommandLine();

	if (!ParseCommandLineParameters(strLine))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFPARSECMDLNERROR], MB_OK);
		TRACE0("Ошибка разбора командной строки\n");
	}

	return 0;
}

LRESULT CMainFrame::OnToolbarReset(WPARAM wp, LPARAM)
{
	// Настройки всех тулбаров: высота
	UpdateToolbarSize();
	// Сброс тулбаров через меню Настройки тулбаров
	ResetToolbar(UINT(wp));
	return S_OK;
}

void CMainFrame::ResetToolbar(UINT uiToolBarId)
{
	if (uiToolBarId == (theApp.m_bHiColorIcons ? SOUND_MENU_HICOLOR : SOUND_MENU_LOCOLOR))
	{
		CSliderButton btnSlider(ID_OPTIONS_SOUND_VOLUME, -1, TBS_TRANSPARENTBKGND);
		btnSlider.SetRange(0, 0xffff);
#ifdef BIG_BUTTONS
		// Поменяем высоту контрола Слайдера
		// CRect rectok, wind;
#endif
		m_wndToolBarSound.ReplaceButton(ID_OPTIONS_SOUND_VOLUME, btnSlider);
	}
	else if (uiToolBarId == (theApp.m_bHiColorIcons ? MAIN_MENU_HICOLOR : MAIN_MENU_LOCOLOR))
	{
		//создаём кнопки с выпадающими меню. 4 привода - 4 разных кнопки с 4мя разными-одинаковыми меню
		CMenu menuA;
		VERIFY(menuA.LoadMenu(IDR_LOADDRIVE_MENU_A));
		CMenu *pSubMenu = menuA.GetSubMenu(0);
		CMFCToolBarMenuButton btnBackA(ID_FILE_LOADDRIVE_A, pSubMenu->GetSafeHmenu(), -1);
		m_wndToolBar.ReplaceButton(ID_FILE_LOADDRIVE_A, btnBackA);
		CMenu menuB;
		VERIFY(menuB.LoadMenu(IDR_LOADDRIVE_MENU_B));
		pSubMenu = menuB.GetSubMenu(0);
		CMFCToolBarMenuButton btnBackB(ID_FILE_LOADDRIVE_B, pSubMenu->GetSafeHmenu(), -1);
		m_wndToolBar.ReplaceButton(ID_FILE_LOADDRIVE_B, btnBackB);
		CMenu menuC;
		VERIFY(menuC.LoadMenu(IDR_LOADDRIVE_MENU_C));
		pSubMenu = menuC.GetSubMenu(0);
		CMFCToolBarMenuButton btnBackC(ID_FILE_LOADDRIVE_C, pSubMenu->GetSafeHmenu(), -1);
		m_wndToolBar.ReplaceButton(ID_FILE_LOADDRIVE_C, btnBackC);
		CMenu menuD;
		VERIFY(menuD.LoadMenu(IDR_LOADDRIVE_MENU_D));
		pSubMenu = menuD.GetSubMenu(0);
		CMFCToolBarMenuButton btnBackD(ID_FILE_LOADDRIVE_D, pSubMenu->GetSafeHmenu(), -1);
		m_wndToolBar.ReplaceButton(ID_FILE_LOADDRIVE_D, btnBackD);
		// И тут-же обновим иконки дисков
		UpdateToolbarDriveIcons();
	}
}
//наглядно отобразим, что и в каком дисководе находится
void CMainFrame::UpdateToolbarDriveIcons()
{
	ChangeImageIcon(ID_FILE_LOADDRIVE_A, CFDDController::BK_FDD_DRIVE_A);
	ChangeImageIcon(ID_FILE_LOADDRIVE_B, CFDDController::BK_FDD_DRIVE_B);
	ChangeImageIcon(ID_FILE_LOADDRIVE_C, CFDDController::BK_FDD_DRIVE_C);
	ChangeImageIcon(ID_FILE_LOADDRIVE_D, CFDDController::BK_FDD_DRIVE_D);
}
// Эта же функция может быть вызвана при изменении размеров иконок кнопкой
void CMainFrame::UpdateToolbarSize()
{
#ifdef BIG_BUTTONS
	//  ResetToolbar(m_wndToolBar.GetDlgCtrlID);      // Get UI ID for toolbar
	//  ResetToolbar(m_wndToolBarSound.GetDlgCtrlID);
	//  ResetToolbar(m_wndToolBarDebug.GetDlgCtrlID);
	m_wndToolBar.SetSizes(CSize(28, 28), CSize(24, 23));
	m_wndToolBarSound.SetSizes(CSize(28, 28), CSize(24, 23));
	m_wndToolBarDebug.SetSizes(CSize(28, 28), CSize(24, 23));
#endif
}


void CMainFrame::OnLVolumeSlider(NMHDR *pNMHDR, LRESULT *pResult)
{
	auto pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);

	if (pNMCD->dwDrawStage == CDDS_PREPAINT)
	{
		auto pSlider = DYNAMIC_DOWNCAST(CSliderButton, m_wndToolBarSound.GetButton(m_wndToolBarSound.CommandToIndex(ID_OPTIONS_SOUND_VOLUME)));
		g_Config.m_nSoundVolume = pSlider->GetValue();

		if (m_pSound)
		{
			m_pSound->SoundGen_SetVolume(g_Config.m_nSoundVolume);
		}
	}

	*pResult = S_OK;
}

bool CMainFrame::CreateDockingWindows()
{
	// Создать окно дампа регистров
	CString strWndName;
	BOOL bNameValid = strWndName.LoadString(IDS_REGDUMP_CPU_WND);
	ASSERT(bNameValid);

	if (!m_paneRegistryDumpViewCPU.Create(strWndName, this, CRect(0, 0, 280, 200), true, ID_VIEW_REGDUMP_CPU, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKREGDUMPVIEWERROR], MB_OK);
		TRACE0("Не удалось создать окно \"Дамп регистров CPU\"\n");
		return false; // не удалось создать
	}

	bNameValid = strWndName.LoadString(IDS_REGDUMP_FDD_WND);
	ASSERT(bNameValid);

	if (!m_paneRegistryDumpViewFDD.Create(strWndName, this, CRect(0, 0, 280, 200), true, ID_VIEW_REGDUMP_FDD, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKREGDUMPVIEWERROR], MB_OK);
		TRACE0("Не удалось создать окно \"Дамп регистров FDD\"\n");
		return false; // не удалось создать
	}

	// Создать окно управления лентами
	bNameValid = strWndName.LoadString(IDS_TAPECTRL_WND);
	ASSERT(bNameValid);

	if (!m_paneTapeCtrlView.Create(strWndName, this, CRect(0, 0, 280, 50), true, ID_VIEW_TAPECTRLWND, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_LEFT | CBRS_FLOAT_MULTI))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKTAPECTRLVIEWERROR], MB_OK);
		TRACE0("Не удалось создать окно \"Управление лентами\"\n");
		return false; // не удалось создать
	}

	// Создать окно отладка
	bNameValid = strWndName.LoadString(IDS_DEBUG_WND);
	ASSERT(bNameValid);

	if (!m_paneDisassembleView.Create(strWndName, this, CRect(0, 0, 350, 200), true, ID_VIEW_DEBUGWND, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKDEBUGVIEWERROR], MB_OK);
		TRACE0("Не удалось создать окно \"Отладка\"\n");
		return false; // не удалось создать
	}

	bNameValid = strWndName.LoadString(IDS_MEMDUMP_WND);
	ASSERT(bNameValid);

	if (!m_paneMemoryDumpView.Create(strWndName, this, CRect(0, 0, 350, 200), true, ID_VIEW_MEMDUMP, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_RIGHT | CBRS_FLOAT_MULTI))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKMEMDUMPVIEWERROR], MB_OK);
		TRACE0("Не удалось создать окно \"Дамп памяти\"\n");
		return false; // не удалось создать
	}

	// Создать окно осциллографа
	bNameValid = strWndName.LoadString(IDS_OSCILLATOR_WND);
	ASSERT(bNameValid);

	if (!m_paneOscillatorView.Create(strWndName, this, CRect(0, 0, 400, 200), true, ID_VIEW_OSCILLATOR, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_BOTTOM | CBRS_FLOAT_MULTI))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKOSCVIEWERROR], MB_OK);
		TRACE0("Не удалось создать окно \"Осциллограф\"\n");
		return false; // не удалось создать
	}

	//Создать окно виртуальной клавиатуры
	bNameValid = strWndName.LoadString(IDS_VKBD_WND);

	if (!m_paneBKVKBDView.Create(strWndName, this, CRect(0, 0, 700, 200), true, ID_VIEW_VKBD, WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_BOTTOM | CBRS_FLOAT_MULTI))
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFDOCKTAPECTRLVIEWERROR], MB_OK);
		TRACE0("Не удалось создать окно \"Виртуальная клавиатура\"\n");
		return false; // не удалось создать
	}

	SetDockingWindowIcons(theApp.m_bHiColorIcons);
	return true;
}

void CMainFrame::SetDockingWindowIcons(bool bHiColorIcons)
{
	auto hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_REGDUMPCPU_WND_HC : IDI_REGDUMPCPU_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_paneRegistryDumpViewCPU.SetIcon(hIcon, FALSE);
	hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_REGDUMPFDD_WND_HC : IDI_REGDUMPFDD_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_paneRegistryDumpViewFDD.SetIcon(hIcon, FALSE);
	hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_MEMDUMP_WND_HC : IDI_MEMDUMP_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_paneMemoryDumpView.SetIcon(hIcon, FALSE);
	hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_DEBUG_WND_HC : IDI_DEBUG_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_paneDisassembleView.SetIcon(hIcon, FALSE);
	hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_TAPE_WND_HC : IDI_TAPE_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_paneTapeCtrlView.SetIcon(hIcon, FALSE);
	hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_OSC_WND_HC : IDI_OSC_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_paneOscillatorView.SetIcon(hIcon, FALSE);
	hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_VKBD_WND_HC : IDI_VKBD_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_paneBKVKBDView.SetIcon(hIcon, FALSE);
}

// диагностика CMainFrame

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
	CFrameWndEx::AssertValid();
}

void CMainFrame::Dump(CDumpContext &dc) const
{
	CFrameWndEx::Dump(dc);
}
#endif //_DEBUG


// обработчики сообщений CMainFrame

void CMainFrame::OnViewCustomize()
{
	auto pDlgCust = new CMFCToolBarsCustomizeDialog(this, TRUE /* сканировать меню*/);

	if (pDlgCust)
	{
		pDlgCust->EnableUserDefinedToolbars();
		pDlgCust->Create();
	}
}

LRESULT CMainFrame::OnToolbarCreateNew(WPARAM wp, LPARAM lp)
{
	LRESULT lres = CFrameWndEx::OnToolbarCreateNew(wp, lp);

	if (lres)
	{
		auto pUserToolbar = reinterpret_cast<CMFCToolBar *>(lres);
		ASSERT_VALID(pUserToolbar);
		CString strCustomize;
		BOOL bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);
		ASSERT(bNameValid);
		pUserToolbar->EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);
	}

	return lres;
}

void CMainFrame::OnApplicationLook(UINT id)
{
	CWaitCursor wait;
	theApp.m_nAppLook = id;

	switch (theApp.m_nAppLook)
	{
		case ID_VIEW_APPLOOK_WIN_2000:
			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManager));
			break;

		case ID_VIEW_APPLOOK_OFF_XP:
			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOfficeXP));
			break;

		case ID_VIEW_APPLOOK_WIN_XP:
			CMFCVisualManagerWindows::m_b3DTabsXPTheme = TRUE;
			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
			break;

		case ID_VIEW_APPLOOK_OFF_2003:
			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOffice2003));
			CDockingManager::SetDockingMode(DT_SMART);
			break;

		case ID_VIEW_APPLOOK_VS_2005:
			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerVS2005));
			CDockingManager::SetDockingMode(DT_SMART);
			break;

		case ID_VIEW_APPLOOK_VS_2008:
			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerVS2008));
			CDockingManager::SetDockingMode(DT_SMART);
			break;

		case ID_VIEW_APPLOOK_WINDOWS_7:
			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows7));
			CDockingManager::SetDockingMode(DT_SMART);
			break;

		default:
			switch (theApp.m_nAppLook)
			{
				case ID_VIEW_APPLOOK_OFF_2007_BLUE:
					CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007_LunaBlue);
					break;

				case ID_VIEW_APPLOOK_OFF_2007_BLACK:
					CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007_ObsidianBlack);
					break;

				case ID_VIEW_APPLOOK_OFF_2007_SILVER:
					CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007_Silver);
					break;

				case ID_VIEW_APPLOOK_OFF_2007_AQUA:
					CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007_Aqua);
					break;
			}

			CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOffice2007));
			CDockingManager::SetDockingMode(DT_SMART);
	}

	RedrawWindow(nullptr, nullptr, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME | RDW_ERASE);
	theApp.WriteInt(_T("ApplicationLook"), theApp.m_nAppLook);
}

void CMainFrame::OnUpdateApplicationLook(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(theApp.m_nAppLook == pCmdUI->m_nID);
}

void CMainFrame::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
	CFrameWndEx::OnSettingChange(uFlags, lpszSection);
}

BOOL CMainFrame::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd *pParentWnd, CCreateContext *pContext)
{
	// базовый класс не работает
	if (!CFrameWndEx::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext))
	{
		return FALSE;
	}

	// включить кнопку настройки для всех пользовательских панелей инструментов
	CString strCustomize;
	BOOL bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);
	ASSERT(bNameValid);

	for (int i = 0; i < iMaxUserToolbars; ++i)
	{
		CMFCToolBar *pUserToolbar = GetUserToolBarByIndex(i);

		if (pUserToolbar != nullptr)
		{
			pUserToolbar->EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);
		}
	}

	//единственно возможный вариант проверки на 2 копии.
	//недостаток - второй экземпляр полностью запускается, полностью отображается на экране.
	//после передачи параметров - закрывается. Как сделать, чтобы не мелькало - непонятно.
	//т.к. в общем-то параметры передавать надо не здесь. А вот обрабатывать - здесь.
	//может как-то удастся перенести всё это в БКАпп, и оттуда передавать сюда командную строку.
	if (!m_mtInstance.Lock(0))
	{
		// Если существует другой экземпляр эмулятора
		// Найдём другое окно
		::PostMessage(HWND_BROADCAST, m_nInterAppGlobalMsg, QUESTION_PRIME_HWND, reinterpret_cast<LPARAM>(GetSafeHwnd()));
		Sleep(500); // Закроем приложение (в PreTranslateMessage)
	}

	return TRUE;
}

LRESULT CMainFrame::OnStartPlatform(WPARAM wParam, LPARAM lParam)
{
	//вызывается после создания CBKView и во время его первой инициализации
	//в функции CBKView::OnInitialUpdate()
	InitKbdStatus();

	if (ProcessFile(true)) //вот здесь только создаётся борда бкашки
	{
		return S_OK;
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFBKBOARDERROR], MB_OK);
		DestroyWindow(); //не создалось - ничего не можем сделать. выходим.
		return S_FALSE;
	}
}

LRESULT CMainFrame::OnDrawDebugScreen(WPARAM wp, LPARAM)
{
	//чтобы видеть как происходит движение луча в процессе прорисовки экрана
	//эти строки нужно закомментировать.
	//а иногда наоборот, очень неудобно когда экран по частям перерисовывается.
	//тогда эти строки надо раскомментировать
	//!!!TODO: может сделать чекбокс, с выбором способа отладочной перерисовки
	m_pScreen->SetRegister(m_pChip->m_reg177664);
	m_pScreen->PrepareScreenRGB32(m_pChip->GetMainMemory() + DWORD_PTR(wp));
	register auto view = GetActiveView();

	if (view)
	{
		view->Invalidate(FALSE);
	}

	return S_OK;
}

LRESULT CMainFrame::OnDrawBKScreen(WPARAM, LPARAM)
{
	register auto view = GetActiveView();

	if (view && view->IsWindowVisible())
	{
		view->Invalidate(FALSE);
	}

	return S_OK;
}

LRESULT CMainFrame::OnDrawBKOscill(WPARAM, LPARAM)
{
	if (m_paneOscillatorView.IsVisible())
	{
		m_paneOscillatorView.GetobjPtr()->Invalidate(FALSE);
	}

	return S_OK;
}

void CMainFrame::InitKbdStatus()
{
	GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_XLAT, false);
	GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_CAPS, !(GetKeyState(VK_CAPITAL) & 1)); //зафиксируем текущее состояние капслока
	GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_AR2, false);
	GetBKVKBDViewPtr()->SetKeyboardStatus(STATUS_PANE_KBD_SU, false);
}

void CMainFrame::InitRegistry()
{
	static initRegRaram regParam[5] =
	{
		// Register BinFile
		{ _T("BKEmulator.BinFile"), _T("binary file"), _T("/b \"%1\""), IDR_MAINFRAME, IDS_FILEEXT_BINARY, true },
		// Register MemFile
		{ _T("BKEmulator.MemFile"), _T("memory state"), _T("/m \"%1\""), IDI_MEMORY, IDS_FILEEXT_MEMORYSTATE, true },
		// Register TapeFle
		{ _T("BKEmulator.TapeFile"), _T("tape file"), _T("/t \"%1\""), IDI_TAPE, IDS_FILEEXT_TAPE, true },
		// Register ScriptFile
		{ _T("BKEmulator.ScriptFile"), _T("script file"), _T("/s \"%1\""), IDI_SCRIPT, IDS_FILEEXT_SCRIPT, true },
		// Register ROMFile
		{ _T("BKEmulator.RomFile"), _T("ROM file"), _T(""), IDI_ROM, IDS_FILEEXT_ROM, false }
	};

	for (auto &i : regParam)
	{
		RegisterDefaultIcon(theApp.m_strProgramName, &i);

		if (&i.bShell)
		{
			RegisterShellCommand(theApp.m_strProgramName, &i);
		}
	}
}

void CMainFrame::RegisterDefaultIcon(CString &strName, initRegRaram *regParam)
{
	CString strTitle(MAKEINTRESOURCE(IDS_EMUL_TITLE));
	CString strIcon = IntToString(regParam->iconID * -1, 10);
	CString strExt(MAKEINTRESOURCE(regParam->nExtStrID));
	HKEY hKey;
	LSTATUS hr = ::RegCreateKey(HKEY_CLASSES_ROOT, strExt, &hKey);
	hr = ::RegSetValue(hKey, nullptr, REG_SZ, regParam->strSection, 0);
	hr = ::RegCloseKey(hKey);
	hr = ::RegCreateKey(HKEY_CLASSES_ROOT, regParam->strSection, &hKey);
	hr = ::RegSetValue(hKey, nullptr, REG_SZ, strTitle + _T(" ") + regParam->strDescription, 0);
	hr = ::RegCloseKey(hKey);
	hr = ::RegCreateKey(HKEY_CLASSES_ROOT, regParam->strSection + _T("\\DefaultIcon"), &hKey);
	hr = ::RegSetValue(hKey, nullptr, REG_SZ, _T("\"") + g_Config.GetCurrentPath() + strName + _T("\",") + strIcon, 0);
	hr = ::RegCloseKey(hKey);
}

void CMainFrame::RegisterShellCommand(CString &strName, initRegRaram *regParam)
{
	HKEY hKey;
	LSTATUS hr = ::RegCreateKey(HKEY_CLASSES_ROOT, regParam->strSection + _T("\\Shell"), &hKey);
	hr = ::RegCloseKey(hKey);
	hr = ::RegCreateKey(HKEY_CLASSES_ROOT, regParam->strSection + _T("\\Shell\\Open"), &hKey);
	hr = ::RegCloseKey(hKey);
	hr = ::RegCreateKey(HKEY_CLASSES_ROOT, regParam->strSection + _T("\\Shell\\Open\\Command"), &hKey);
	hr = ::RegSetValue(hKey, nullptr, REG_SZ, _T("\"") + g_Config.GetCurrentPath() + strName + _T("\" ") + regParam->strArguments, 0);
	hr = ::RegCloseKey(hKey);
}

BOOL CMainFrame::PreTranslateMessage(MSG *pMsg)
{
	if (pMsg->message == m_nInterAppGlobalMsg)
	{
		HWND hwnd = GetSafeHwnd();

		//обработка сообщения, которым обмениваются между собой копии приложения
		switch (pMsg->wParam)
		{
			case QUESTION_PRIME_HWND:
			{
				//основная прога обрабатывает запрос копии
				//если другая копия проги спрашивает эту копию, надо ей ответить, и послать
				//хэндл окна этой копии
				auto copy_hwnd = reinterpret_cast<HWND>(pMsg->lParam);

				//поскольку мы разослали сообщение брудкастом, то и сами себе его получили, поэтому
				//если copy_hwnd == hwnd, то получили сообщение сами от себя, нафиг оно нам надо?
				if (copy_hwnd != hwnd)
				{
					//копия проги не должна самой себе посылать ответ
					::PostMessage(copy_hwnd, m_nInterAppGlobalMsg, ANSWER_PRIME_HWND, reinterpret_cast<LPARAM>(hwnd));
				}

				return TRUE;
			}

			case ANSWER_PRIME_HWND:
			{
				//копия обрабатывает ответ от основной проги
				//тут мы получили ответ от первой копии проги, получили хэндл окна
				CString strCommands = GetCommandLine();
				//теперь наша задача, отправить параметры командной строки основной копии проги.
				//
				// Fill the COPYDATA structure
				//
				TCHAR pInstanceCommandPrompt[1024];
				memcpy(pInstanceCommandPrompt, strCommands.GetBuffer(sizeof(pInstanceCommandPrompt)), sizeof(pInstanceCommandPrompt));
				COPYDATASTRUCT cd;
				cd.dwData = 2;
				cd.cbData = sizeof(pInstanceCommandPrompt);
				cd.lpData = reinterpret_cast<PVOID>(pInstanceCommandPrompt);
				// вызываем функцию, передающую данные в &cd основной копии проги
				::SendMessage(reinterpret_cast<HWND>(pMsg->lParam), WM_COPYDATA, reinterpret_cast<WPARAM>(hwnd), reinterpret_cast<LPARAM>(&cd));
				strCommands.ReleaseBuffer();
				DestroyWindow(); //всё, делать больше нечего. киляемся
				return TRUE;
			}
		}
	}

	if (pMsg->message == WM_SYSKEYDOWN && pMsg->wParam == VK_MENU)
	{
		// Если нажали любой Alt
		return FALSE;   //будем обрабатывать сами
	}

	if (pMsg->message == WM_SYSKEYUP && pMsg->wParam == VK_MENU)
	{
		// Если отжали любой Alt
		return FALSE;   //будем обрабатывать сами
	}

	return CFrameWndEx::PreTranslateMessage(pMsg);
}

bool CMainFrame::ParseCommandLineParameters(CString strCommands)
{
	ClearProcessingFiles();
	int nFindPos = strCommands.Find(_T('/'));

	if (nFindPos == -1)
	{
		return true; //ключей нету, разбирать нечего, просто выходим.
	}

	//если ключи есть, оставляем только их.
	strCommands = strCommands.Right(strCommands.GetLength() - max(0, nFindPos));
	int nStrLen; //текущая длина оставшейся неразобранной строки

	while (nStrLen = strCommands.GetLength())
	{
		nFindPos = strCommands.Find(_T('/'), 1); //ищем, есть ли ещё ключи

		if (nFindPos == -1) //если нету
		{
			nFindPos = nStrLen; //то всё что есть - параметр данного ключа
		}

		CString strParam = strCommands.Left(nFindPos); //выделяем параметр в отдельную строку
		strCommands = strCommands.Right(nStrLen - nFindPos); //и отсекаем их от обрабатываемой строки.
		strParam.Trim(); //уберём пробелы, если есть
		TCHAR command = toupper(strParam[1]); //получим значение ключа
		strParam = strParam.Right(strParam.GetLength() - 2); //выделим аргументы
		strParam.Trim(); //снова уберём пробелы. теперь они точно есть
		strParam.Trim(_T('\"')); //а ещё кавычки

		switch (command)
		{
			case _T('B'): // Binary file
			{
				m_strBinFileName = g_Config.GetFileName(strParam);
				CString strFilePath = g_Config.GetFilePath(strParam);

				if (!strFilePath.IsEmpty())
				{
					g_Config.m_strBinPath = strFilePath;
				}

				m_strScriptFileName = _T("autorun\\monitor_load.bkscript");
				g_Config.m_bEmulateLoadTape = true;
			}
			break;

			case _T('M'): // Memory state file
			{
				m_strMemFileName = g_Config.GetFileName(strParam);
				CString strFilePath = g_Config.GetFilePath(strParam);

				if (!strFilePath.IsEmpty())
				{
					g_Config.m_strMemPath = strFilePath;
				}
			}
			break;

			case _T('T'): // BK tape file
			{
				m_strTapeFileName = g_Config.GetFileName(strParam);
				CString strFilePath = g_Config.GetFilePath(strParam);

				if (!strFilePath.IsEmpty())
				{
					g_Config.m_strTapePath = strFilePath;
				}
			}
			break;

			case _T('S'): // Initial Script file
			{
				m_strScriptFileName = g_Config.GetFileName(strParam);
				CString strFilePath = g_Config.GetFilePath(strParam);

				if (!strFilePath.IsEmpty())
				{
					g_Config.m_strScriptsPath = strFilePath;
				}
			}
			break;

			case _T('?'):
			case _T('H'):
			{
				CString strTitle(MAKEINTRESOURCE(IDS_EMUL_TITLE));
				CString strPrompt(MAKEINTRESOURCE(IDS_COMMAND_PROMPT_INFO));
				CBKMessageBox(strTitle + strPrompt, MB_OK);
				return true;
			}

			case _T('W'):
				//не знаю что такое, так и было
				break;
		}
	}

	return true;
}


void CMainFrame::StartPlayTape(const CString &strPath)
{
	CString strExt = g_Config.GetFileExt(strPath);
	TAPE_FILE_INFO tfi;
	memset(&tfi, 255, sizeof(TAPE_FILE_INFO));

	if (!strExt.CompareNoCase(CString(MAKEINTRESOURCE(IDS_FILEEXT_WAVE))))
	{
		m_tape.LoadWaveFile(strPath);
	}
	else if (!strExt.CompareNoCase(CString(MAKEINTRESOURCE(IDS_FILEEXT_TAPE))))
	{
		m_tape.LoadMSFFile(strPath);
	}
	else if (!strExt.CompareNoCase(CString(MAKEINTRESOURCE(IDS_FILEEXT_BINARY))))
	{
		m_tape.LoadBinFile(strPath, &tfi);
	}
	else
	{
		return;
	}

	m_tape.GetWaveFile(&tfi); //вычисляем и заполняем внутрениие переменные
	m_tape.SetPlayWavePos(0);
	m_tape.StartPlay();
}


void CMainFrame::UpdateTapeDlgControls()
{
	m_paneTapeCtrlView.UpdateTapeControls(g_Config.m_bEmulateLoadTape, g_Config.m_bEmulateSaveTape);
}

void CMainFrame::OnClose()
{
	//тут надо занести в конфиг разные переменные и параметры опций, которые надо сохранить
	g_Config.m_nCPUFrequency = m_pChip->GetCPUSpeed();
	StopAll();
	CheckDebugMemmap(); //если карта памяти была открыта - закроем
	CFrameWndEx::OnClose();
}

void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
	switch (nIDEvent)
	{
		case BKTIMER_UI_REFRESH:
			OnMainLoop();
			break;
	}
}

/////////////////////////////////////////////////////////////////////////////
//по идее, вызывается каждые 20 миллисекунд, обновляет информацию в
//пользовательском интерфейсе
void CMainFrame::OnMainLoop()
{
	if (m_pChip == nullptr)   //если чипа нету, ничего не делать
	{
		return;
	}

	register DWORD nTmpTick;

	if ((nTmpTick = GetTickCount()) - m_nTickCounter >= 1000)
	{
		m_nTickCounter = nTmpTick;
		nTmpTick = (nTmpTick - m_nStartTick) / 1000;
		//таймер аптайма. поточнее, но из-за того, что OnMainLoop() вызывается не точно через 20мс,
		//и сюда попадаем иногда больше чем через секунду, то при выводе иногда счётчик перескакивает
		//через секунду. Но тем не менее, тут показывается реальное время работы, а не счётчик попаданий,
		//как раньше.
		register DWORD seconds = nTmpTick % 60;
		register DWORD minutes = (nTmpTick / 60) % 60;
		register DWORD hours = nTmpTick / (60 * 60);

		if (m_pScreen->IsFullScreenMode()) //если мы в полноэкранном режиме
		{
#ifdef TARGET_WINXP
			//для D3D9 таких проблем нету
			SetFocusToBK(); // будем принудительно передавать фокус Экрану
#else

			//если в полноэкранном режиме и рендер не D3D, то только тогда
			if (g_Config.m_nScreenRenderType != CONF_SCREEN_RENDER_D3D)
			{
				SetFocusToBK(); //будем принудительно передавать фокус Экрану
			}

			// Тут возникла проблема с полноэкранным режимом D3D11.
			// если мы передаём фокус CBKView - вываливаемся из режима. Потому что
			// в полноэкранном режиме фокус должен быть у CScreen, как только он будет потерян
			// тут же выпадаем из полноэкранного режима
#endif
		}

		if (g_Config.m_bShowPerformance) //если в оконном и включена опция показа информации
		{
			// Показываем производительность в строке состояния
			CString strPerformance;
			strPerformance.Format(_T("Scr: %d x %d, FPS: %i, Uptime %02i:%02i:%02i, CPU: %d Hz"), m_nScreen_X, m_nScreen_Y, m_pScreen->GetFPS(), hours, minutes, seconds, m_pChip->GetCPUFreq());
			m_wndStatusBar.SetPaneText(STATUS_PANE_INFO, strPerformance);
		}
	}

	//карту памяти тоже тут обновляем
	if (m_bBKMemViewOpen)
	{
		m_pBKMemView->DrawTab();
	}

	//если проц остановлен, то информацию не обновлять, т.к. она принудительно обновляется при пошаговой отладке.
	//а если работает - то обновлять
	if (!m_pChip->IsCPUBreaked())
	{
		//это имеет смысл только в информативных целях, иначе звук запинается на слабых конфигурациях
		// причём эту штуку нельзя поместить в основной цикл. потому что где-то то-то недоработано.
		// цикл запускается раньше, чем проинициализируются все элементы.
		// прога валится на CDebuggere.
		if (g_Config.m_nRegistersDumpInterval && !m_pScreen->IsFullScreenMode())
		{
			if (--m_nRegsDumpCounter <= 0)
			{
				m_nRegsDumpCounter = g_Config.m_nRegistersDumpInterval;
				SetDebugCtrlsState();
			}
		}
	}
}


BOOL CMainFrame::OnCopyData(CWnd *pWnd, COPYDATASTRUCT *pCopyDataStruct)
{
	if (pCopyDataStruct->dwData == 2)
	{
		CString strLine(reinterpret_cast<LPCTSTR>(pCopyDataStruct->lpData), pCopyDataStruct->cbData);
		bool bRes = false;

		if (ParseCommandLineParameters(strLine))
		{
			bRes = ProcessFile(false);
		}

		SetForegroundWindow();
		return bRes;
	}

	return CFrameWnd::OnCopyData(pWnd, pCopyDataStruct);
}


LRESULT CMainFrame::OnDropFile(WPARAM wParam, LPARAM lParam)
{
	auto strPath = reinterpret_cast<CString *>(lParam);
	ClearProcessingFiles();
	CString strFilePath = g_Config.GetFilePath(*strPath);
	CString strFileExt = g_Config.GetFileExt(*strPath);
	CString strBinExt(MAKEINTRESOURCE(IDS_FILEEXT_BINARY));
	CString strMemExt(MAKEINTRESOURCE(IDS_FILEEXT_MEMORYSTATE));
	CString strTapExt(MAKEINTRESOURCE(IDS_FILEEXT_TAPE));
	CString strWavExt(MAKEINTRESOURCE(IDS_FILEEXT_WAVE));
	CString strScptExt(MAKEINTRESOURCE(IDS_FILEEXT_SCRIPT));

	if (!strFileExt.CompareNoCase(strBinExt))  // Binary file
	{
		m_strBinFileName = g_Config.GetFileName(*strPath);

		if (!strFilePath.IsEmpty())
		{
			g_Config.m_strBinPath = strFilePath;
		}

		m_strScriptFileName = _T("autorun\\monitor_load.bkscript");
		g_Config.m_bEmulateLoadTape = true;
	}
	else if (!strFileExt.CompareNoCase(strMemExt))  // Memory state file
	{
		m_strMemFileName = g_Config.GetFileName(*strPath);

		if (!strFilePath.IsEmpty())
		{
			g_Config.m_strMemPath = strFilePath;
		}
	}
	else if (strFileExt.CompareNoCase(strTapExt) == 0
	         || strFileExt.CompareNoCase(strWavExt) == 0)  // BK tape file
	{
		m_strTapeFileName = g_Config.GetFileName(*strPath);

		if (!strFilePath.IsEmpty())
		{
			g_Config.m_strTapePath = strFilePath;
		}
	}
	else if (!strFileExt.CompareNoCase(strScptExt))  // Initial Script file
	{
		m_strScriptFileName = g_Config.GetFileName(*strPath);

		if (!strFilePath.IsEmpty())
		{
			g_Config.m_strScriptsPath = strFilePath;
		}
	}
	else
	{
		ASSERT(FALSE);
		return S_FALSE;
	}

	ProcessFile(false); //здесь если надо создаётся новая борда бкашки, а если нет - остаётся текущая
	return S_OK;
}


void CMainFrame::ClearProcessingFiles()
{
	m_strBinFileName.Empty();
	m_strMemFileName.Empty();
	m_strTapeFileName.Empty();
	m_strScriptFileName.Empty();
}

LRESULT CMainFrame::OnOutKeyboardStatus(WPARAM wParam, LPARAM lParam)
{
	auto pane = PANE_STATUS(wParam);
	auto str = reinterpret_cast<CString *>(lParam);
	m_wndStatusBar.SetPaneText(pane, *str);
	return S_OK;
}

void CMainFrame::SetupConfiguration(CONFIG_BKMODEL_NUMBER nConf)
{
	if (ConfigurationConstructor(nConf))
	{
		SetFocusToBK();
	}
	else
	{
		//надо вообще закрыть прогу. нечего тут делать.
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFBKBOARDERROR], MB_OK);
		theApp.ExitInstance();
	}
}

bool CMainFrame::ProcessFile(bool type)
{
	m_script.SetScriptPath(g_Config.m_strScriptsPath, m_strScriptFileName);
	bool bRes = false;

	// Инициализация БК чипа
	if (!m_strMemFileName.IsEmpty()) //если задан .msf файл, то пробуем загрузить его
	{
		// Если не можем загрузить msf файл
		if (!(bRes = LoadMemoryState(g_Config.m_strMemPath + m_strMemFileName)))
		{
			bRes = ConfigurationConstructor(CONFIG_BKMODEL_NUMBER_BK_0010_01);    // Создаём конфигурацию по умолчанию
		}
	}
	else
	{
		//иначе, смотрим, что там.
		if (type) //если вызвали из onCreate - то создаём заданную модель
		{
			bRes = ConfigurationConstructor(g_Config.GetBKModelNumber());  // Создаём заданную конфигурацию
		}
		else
		{
			//если вызвали из ДрагнДропа - то
			// Если задан bin файл
			if (!m_strBinFileName.IsEmpty())
			{
				bRes = ConfigurationConstructor(CONFIG_BKMODEL_NUMBER_BK_0010_01);    // Создаём конфигурацию по умолчанию
			}

			//если bin файл не задан, то вообще ничего не создаём.
		}
	}

	if (!type || bRes)
	{
		// Если задан файл ленты, запускаем его играться
		if (!m_strTapeFileName.IsEmpty())
		{
			StartPlayTape(g_Config.m_strTapePath + m_strTapeFileName);
		}
	}

	SetFocusToBK();
	return bRes;
}

void CMainFrame::InitEmulator()
{
	UpdateFrameTitleForDocument(g_mstrConfigBKModelParameters[g_Config.GetBKModelNumber()].strBKModelName);

	switch (g_Config.m_nVKBDType)
	{
		default:
		case 0:
			m_paneBKVKBDView.SetKeyboardView(IDB_BITMAP_SOFT);
			break;

		case 1:
			m_paneBKVKBDView.SetKeyboardView(IDB_BITMAP_PLEN);
			break;
	}

	auto pSlider = DYNAMIC_DOWNCAST(CSliderButton, m_wndToolBarSound.GetButton(m_wndToolBarSound.CommandToIndex(ID_OPTIONS_SOUND_VOLUME)));
	pSlider->SetValue(g_Config.m_nSoundVolume);
	m_pSound->SoundGen_SetVolume(g_Config.m_nSoundVolume); //продублируем для надёжности
	// инициализация текущих настроек
	m_speaker.EnableSound(g_Config.m_bSpeaker);
	m_speaker.SetFilter(g_Config.m_bSpeakerFilter);
	m_covox.EnableSound(g_Config.m_bCovox);
	m_covox.SetFilter(g_Config.m_bCovoxFilter);
	m_covox.SetStereo(g_Config.m_bStereoCovox);
	m_ay8910.EnableSound(g_Config.m_bAY8910);
	m_ay8910.SetFilter(g_Config.m_bAY8910Filter);
	m_ay8910.SetStereo(true); //всегда стерео
	//параметры экрана
	m_pScreen->SetColorMode(g_Config.m_bColorMode);
	m_pScreen->SetAdaptMode(g_Config.m_bAdaptBWMode);
	m_pScreen->SetLuminoforeEmuMode(g_Config.m_bLuminoforeEmulMode);
	g_Config.m_bFullscreenMode ? m_pScreen->SetFullScreenMode() : m_pScreen->SetWindowMode();
	m_tape.SetWaveParam(g_Config.m_nSoundSampleRate, BUFFER_CHANNELS);
	m_tape.SetPlayWavePos(0);
	// Настройка панели управления записью
	m_paneTapeCtrlView.InitParams(g_Config.m_strTapePath, &m_tape);
	UpdateTapeDlgControls();
	//наглядно отобразим, что и в каком дисководе находится
	UpdateToolbarDriveIcons();
	InitKbdStatus(); //переинициализируем состояния клавиатуры
	UpdateData(FALSE);
}

//выносим это в отдельную процедуру, т.к. есть вероятность неудачного прочтения MSF,
//при этом надо восстановить всё как было раньше
void CMainFrame::AttachObjects()
{
	auto vw = DYNAMIC_DOWNCAST(CBKView, GetActiveView());

	if (vw)
	{
		vw->ReCreateSCR(); //пересоздаём экран с новыми параметрами.
	}

	m_paneOscillatorView.ReCreateOSC(); //пересоздаём осциллограф с новыми параметрами
	m_pSound->ReInit(); //пересоздаём звук с новыми параметрами
	m_speaker.ReInit(); //ещё надо переинициализирвоать устройства, там
	m_covox.ReInit();   //есть вещи, зависящие от частоты дискретизации,
	m_ay8910.ReInit();  //которая теперь величина переменная. Но требует перезапуска конигурации.

	if (m_pChip)
	{
		m_pChip->AttachWindow(this);  //цепляем к MotherBoard этот класс
		// порядок имеет значение. сперва нужно делать обязательно AttachWindow(this)
		// и только затем m_pChip->GetMTC(). И эта функция обязательна, там звуковой буфер вычисляется
		// и выделяется
		int nMtc = m_pChip->GetMTC();
		//переопределяем буферы в зависимости от текущей частоты дискретизации
		m_speaker.SetSampleBuffer(nMtc);
		m_covox.SetSampleBuffer(nMtc);
		m_ay8910.SetSampleBuffer(nMtc);
		//Присоединяем к новосозданному чипу устрйства
		m_pChip->AttachSound(m_pSound);
		m_pChip->AttachSpeaker(&m_speaker);
		m_pChip->AttachCovox(&m_covox);
		m_pChip->AttachAY8910(&m_ay8910);

		//если в ини файле задана частота, то применим её, вместо частоты по умолчанию.
		if (g_Config.m_nCPUFrequency)
		{
			//в этой функции проверяются границы, которые вычисляются в функции GetMTC.
			//поэтому нужно соблюдать порядок вызова, эти переменные не инициализируются значениями по умолчанию.
			m_pChip->SetCPUFreq(g_Config.m_nCPUFrequency);
		}

		// Цепляем к новому чипу отладчик, т.е. наоборот. к отладчику чип
		m_pDebugger->AttachChip(m_pChip);
		m_paneRegistryDumpViewCPU.SetFreqParam();
		// Цепляем обработчик скриптов
		m_script.AttachChip(m_pChip);
	}
}

bool CMainFrame::ConfigurationConstructor(CONFIG_BKMODEL_NUMBER nConf, bool bStart)
{
	bool bReopenMemMap = false;

	if (m_pChip)
	{
		// Если конфигурация уже существует, удалим её
		StopAll();  //сперва всё остановим
		bReopenMemMap = CheckDebugMemmap(); //флаг переоткрытия карты памяти
		//перед сохранением настройки флагов заберём из диалога
		g_Config.SaveConfig();
		SAFE_DELETE(m_pChip);   //удалим конфигурацию
	}

	g_Config.SetBKModelNumber(nConf);

	// создадим новую конфигурацию
	switch (g_Config.m_BKBoardModel)
	{
		case MSF_CONF_BK1001:
			m_pChip = new CMotherBoard;
			break;

		case MSF_CONF_BK1001_MSTD:
			m_pChip = new CMotherBoard_MSTD;
			break;

		case MSF_CONF_BK1001_EXT32:
			m_pChip = new CMotherBoard_EXT32;
			break;

		case MSF_CONF_BK1001_FDD:
			m_pChip = new CMotherBoard_10_FDD;
			break;

		case MSF_CONF_BK11:
			m_pChip = new CMotherBoard_11;
			break;

		case MSF_CONF_BK11_FDD:
			m_pChip = new CMotherBoard_11_FDD;
			break;

		case MSF_CONF_BK11M:
			m_pChip = new CMotherBoard_11M;
			break;

		case MSF_CONF_BK11M_FDD:
			m_pChip = new CMotherBoard_11M_FDD;
			break;

		default:
			ASSERT(FALSE);
			return false;
	}

	if (!m_pChip)
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
		return false;
	}

	m_pChip->SetFDDType(g_Config.m_BKFDDModel);
	g_Config.LoadConfig(false); //Читаем из ини файла параметры
	AttachObjects();    //пересоздадим и присоединим необходимые устройства.

	if (!m_pChip->InitBoard(g_Config.m_nCPURunAddr))
	{
		// если ресет не удался - значит не удалось проинициализировать
		// память - значит не удалось загрузить какие-то дампы прошивок -
		// значит дальше работать невозможно.
		SAFE_DELETE(m_pChip);
		return false;
	}

	InitEmulator();     //переинициализируем модель
	m_pChip->EnableAskForBreak(g_Config.m_bAskForBreak);

	if (bReopenMemMap)      //если надо
	{
		OnDebugMemmap();    //заново откроем карту памяти
	}

	if (bStart)
	{
		m_pChip->StartTimerThread();
		StartTimer();
		// Запускаем CPU
		m_pChip->RunCPU();

		// если не установлен флаг остановки после создания
		if (g_Config.m_bPauseCPUAfterStart)
		{
			m_pChip->BreakCPU();
		}
	}

	SetFocusToBK();
	return true;
}
//упрощённый вариант функции для загрузки конфигурации
bool CMainFrame::ConfigurationConstructor_LoadConf(CONFIG_BKMODEL_NUMBER nConf)
{
	g_Config.SetBKModelNumber(nConf);

	// создадим новую конфигурацию
	switch (g_Config.m_BKBoardModel)
	{
		case MSF_CONF_BK1001:
			m_pChip = new CMotherBoard;
			break;

		case MSF_CONF_BK1001_MSTD:
			m_pChip = new CMotherBoard_MSTD;
			break;

		case MSF_CONF_BK1001_EXT32:
			m_pChip = new CMotherBoard_EXT32;
			break;

		case MSF_CONF_BK1001_FDD:
			m_pChip = new CMotherBoard_10_FDD;
			break;

		case MSF_CONF_BK11:
			m_pChip = new CMotherBoard_11;
			break;

		case MSF_CONF_BK11_FDD:
			m_pChip = new CMotherBoard_11_FDD;
			break;

		case MSF_CONF_BK11M:
			m_pChip = new CMotherBoard_11M;
			break;

		case MSF_CONF_BK11M_FDD:
			m_pChip = new CMotherBoard_11M_FDD;
			break;

		default:
			ASSERT(FALSE);
			return false;
	}

	if (!m_pChip)
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
		return false;
	}

	m_pChip->SetFDDType(g_Config.m_BKFDDModel);
	//присоединим устройства, чтобы хоть что-то было для выполнения ResetHot
	AttachObjects();

	if (!m_pChip->InitBoard(g_Config.m_nCPURunAddr))
	{
		// если ресет не удался - значит не удалось проинициализировать
		// память - значит не удалось загрузить какие-то дампы прошивок -
		// значит дальше работать невозможно.
		SAFE_DELETE(m_pChip);
		return false;
	}

	m_pChip->EnableAskForBreak(g_Config.m_bAskForBreak);
	return true;
}

LRESULT CMainFrame::OnFinishEmulateTape(WPARAM, LPARAM)
{
	m_paneBKVKBDView.SetKeyboardStatus(STATUS_PANE_KBD_AR2, false);
	m_paneBKVKBDView.SetKeyboardStatus(STATUS_PANE_KBD_SHIFT, false);
	m_paneBKVKBDView.SetKeyboardStatus(STATUS_PANE_KBD_SU, false);
	SetFocusToBK();
	return S_OK;
}

bool CMainFrame::LoadMemoryState(CString &strPath)
{
	CMSFManager msf;
	bool bRet = false;

	if (!msf.OpenFile(strPath, true))
	{
		return bRet;
	}

	if (msf.GetType() == MSF_STATE_ID && msf.GetVersion() >= MSF_VERSION_MINIMAL)
	{
		StopAll();
		bool bReopenMemMap = CheckDebugMemmap(); //флаг переоткрытия карты памяти

		//временно выгрузим все образы дискет.
		if (m_pChip->GetFDDType() != DEV_FDD_NONE)
		{
			m_pChip->GetFDD()->DetachDrives();
		}

		// Сохраняем старую конфигурацию
		CMotherBoard *pOldChip = m_pChip;
		CONFIG_BKMODEL_NUMBER nOldConf = g_Config.GetBKModelNumber();
		m_pChip = nullptr;

		if (ConfigurationConstructor_LoadConf(msf.GetConfiguration()))
		{
			if (m_pChip->RestoreState(msf, nullptr))
			{
				SAFE_DELETE(pOldChip);
				bRet = true;
			}
			else
			{
				//не удалось восстановить состояние, надо вернуть старую кофигурацию.
				CBKMessageBox(IDS_ERRMSF_WRONG, MB_OK);
				SAFE_DELETE(m_pChip);
				m_pChip = pOldChip;
				g_Config.SetBKModelNumber(nOldConf);
				g_Config.LoadConfig();      //восстановим из ини файла параметры
			}

			//приаттачим все образы дискет.
			if (m_pChip->GetFDDType() != DEV_FDD_NONE)
			{
				m_pChip->GetFDD()->ReadDrivesPath();
			}

			AttachObjects();            //переприсоединим устройства, уже с такой, какой надо конфигурацией
			InitEmulator();             //переинициализируем модель
		}
		else
		{
			// Неподдерживаемая конфигурация, или ошибка при создании
			CBKMessageBox(IDS_ERRMSF_WRONG, MB_OK);
			m_pChip = pOldChip;
			g_Config.SetBKModelNumber(nOldConf);
			AttachObjects();
		}

		if (bReopenMemMap && !m_bBKMemViewOpen)
		{
			OnDebugMemmap();
		}

		StartAll();
	}
	else
	{
		CBKMessageBox(IDS_ERRMSF_OLD, MB_OK);
	}

	return bRet;
}


bool CMainFrame::SaveMemoryState(CString &strPath)
{
	if (m_pChip)
	{
		CMSFManager msf;
		msf.SetConfiguration(g_Config.GetBKModelNumber());

		if (!msf.OpenFile(strPath, false))
		{
			return false;
		}

		StopTimer();
		m_pChip->StopCPU(false);
		m_pChip->RestoreState(msf, m_pScreen->GetScreenshot());
		m_pChip->RunCPU(false);
		StartTimer();
	}

	return true;
}

/* оставим код на память.
bool CMainFrame::MakeScreenShot()
{
    bool bRet = false;
    //это не совсем честно. на самом деле BK_SCREEN_WIDTH х BK_SCREEN_HEIGHT - это viewport экрана
    //а оригинальный размер формируемой текстуры экрана - 512х256
    // просто такой скриншот выглядит не очень красиво, слишком мелкий и сплющенный по высоте.
    HBITMAP hBitmap = (HBITMAP)CopyImage(m_pScreen->GetScreenshot(), IMAGE_BITMAP, BK_SCREEN_WIDTH, BK_SCREEN_HEIGHT, 0);

    CString strName;
    strName.Format(_T("screenshot_%d.bmp"), g_Config.m_nScreenshotNumber++);
    CString szFileName = g_Config.m_strScreenShotsPath + strName;

    CFile File;
    BITMAPINFO *pbmi;
    BITMAPFILEHEADER bfh;
    BITMAP bmp;

    //Получение параметров рисунка
    if (!GetObject(hBitmap, sizeof(bmp), &bmp))
        return false;

    //Количество битов под пиксель
    uint16_t wClrBits = (uint16_t)(bmp.bmPlanes*bmp.bmBitsPixel);
    if (wClrBits == 1);
    else if (wClrBits <= 4)
        wClrBits = 4;
    else if (wClrBits <= 8)
        wClrBits = 8;
    else if (wClrBits <= 16)
        wClrBits = 16;
    else if (wClrBits <= 24)
        wClrBits = 24;
    else if (wClrBits <= 32)
        wClrBits = 32;

    //Выделение памяти для BITMAPINFO
    if (wClrBits != 24)
    {
        pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<<wClrBits));
    }
    else
    {
        pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
    }
    //Заполнение BITMAPINFO
    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biHeight = bmp.bmHeight;
    pbmi->bmiHeader.biWidth = bmp.bmWidth;
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
    if (wClrBits < 24)
        pbmi->bmiHeader.biClrUsed = (1<<wClrBits);
    pbmi->bmiHeader.biCompression = BI_RGB;

    //..количество памяти, необходимое для таблицы цветов
    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * wClrBits + 31) & ~31) / 8 * pbmi->bmiHeader.biHeight;
    pbmi->bmiHeader.biClrImportant = 0;

    //Получение памяти под таблицу цветов и массив индексов
    LPBYTE lpData = new uint8_t[pbmi->bmiHeader.biSizeImage];
    if (lpData)
    {
        //Копирование таблицы цветов в выделенную область памяти
        HDC hDC = GetDC()->GetSafeHdc();
        HDC hTempDC = CreateCompatibleDC(hDC);

        SelectObject(hTempDC, hBitmap);
        if (GetDIBits(hTempDC, hBitmap, 0, (uint16_t)bmp.bmHeight, lpData, pbmi, DIB_RGB_COLORS))
        {
            //Создание файла
            _tmkdir(g_Config.m_strScreenShotsPath);
            if (File.Open(szFileName, CFile::modeCreate | CFile::shareExclusive | CFile::modeWrite))
            {
                //Заполняем структуру информации о файле
                bfh.bfType = 0x4d42;
                //Смещение данных рисунка от начала файла
                bfh.bfOffBits = (DWORD)(sizeof(BITMAPFILEHEADER) + pbmi->bmiHeader.biSize +
                    pbmi->bmiHeader.biClrUsed * sizeof(RGBQUAD));
                bfh.bfSize = bfh.bfOffBits + pbmi->bmiHeader.biSizeImage;
                bfh.bfReserved1 = 0;
                bfh.bfReserved2 = 0;

                //Запись данных в файл, ошибки записи проигнорируем. ну и их нафиг, скриншот же.
                //..BITMAPFILEHEADER
                File.Write(&bfh, sizeof(bfh));
                //..BITMAPINFOHEADER и RGBQUAD
                File.Write(&pbmi->bmiHeader, sizeof(BITMAPINFOHEADER) + pbmi->bmiHeader.biClrUsed * sizeof(RGBQUAD));
                //..массив цветов и индексов
                File.Write(lpData, pbmi->bmiHeader.biSizeImage);
                //Запись закончена
                File.Close();
                bRet = true;
            }
        }
        DeleteDC(hTempDC);
        delete lpData;
    }
    DeleteObject(hBitmap);
    return bRet;
}
*/

bool CMainFrame::MakeScreenShot()
{
	bool bRet = false;
	//это не совсем честно. на самом деле BK_SCREEN_WIDTH х BK_SCREEN_HEIGHT - это viewport экрана
	//а оригинальный размер формируемой текстуры экрана - 512х256
	// просто такой скриншот выглядит не очень красиво, слишком мелкий и сплющенный по высоте.
	auto hBitmap = (HBITMAP)CopyImage(m_pScreen->GetScreenshot(), IMAGE_BITMAP, CScreen::BK_SCREEN_WIDTH, CScreen::BK_SCREEN_HEIGHT, 0);

	if (hBitmap)
	{
		CString strName;
		strName.Format(_T("screenshot_%d.png"), g_Config.m_nScreenshotNumber++);
		CString szFileName = g_Config.m_strScreenShotsPath + strName;

		if (OpenClipboard())
		{
			HANDLE hRet = SetClipboardData(CF_BITMAP, hBitmap);
#ifdef _DEBUG

			if (!hRet) //нужно для отладки, когда что-то идёт не так
			{
				GetLastErrorOut(_T("SetClipboardData"));
			}

#endif
			CloseClipboard();
		}

		_tmkdir(g_Config.m_strScreenShotsPath);
		CImage image; //оказывается, в MFC есть вот такая херота.
		image.Attach(hBitmap, CImage::DIBOR_DEFAULT);

		if (SUCCEEDED(image.Save(szFileName, Gdiplus::ImageFormatPNG))) //и как всё оказывается просто.
		{
			bRet = true;
		}

		DeleteObject(hBitmap);
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	return bRet;
}

bool CMainFrame::CheckDebugMemmap()
{
	bool bRet = m_bBKMemViewOpen;

	if (bRet)   //если была открыта карта памяти
	{
		m_bBKMemViewOpen = false;
		m_pBKMemView->GetWindowRect(m_rectMemMap); //а пока закроем
		m_pBKMemView->DestroyWindow();
	}

	return bRet;    //скажем, что после её надо будет переоткрыть
}

void CMainFrame::SetDebugCtrlsState()
{
	if (m_pChip)
	{
		m_paneRegistryDumpViewCPU.DisplayRegDump();
		m_paneRegistryDumpViewFDD.DisplayRegDump();
		//тормозит, если много строк на экране.
		m_paneMemoryDumpView.DisplayMemDump();
	}
}


void CMainFrame::ChangeImageIcon(UINT nBtnID, int nDrive)
{
	if (m_pChip)
	{
		int iImage = (m_pChip->GetFDD()->IsAttached(nDrive)) ?
		             GetCmdMgr()->GetCmdImage(ID_FILE_LOADEDDRIVE) :
		             GetCmdMgr()->GetCmdImage(ID_FILE_EMPTYDRIVE);
		int nIndex = m_wndToolBar.CommandToIndex(nBtnID);
		m_wndToolBar.GetButton(nIndex)->SetImage(iImage);
		m_wndToolBar.InvalidateButton(nIndex); //Перерисовываем только нужную кнопку
//		m_wndToolBar.Invalidate(FALSE); //перерисовываем весь тулбар, а то какие-то непонятные явления в Вин8 случаются:
		//не желает перерисовываться кнопка.
	}
}

void CMainFrame::LoadFileImage(UINT nBtnID, int nDrive)
{
	CString strFilterIMG(MAKEINTRESOURCE(IDS_FILEFILTER_BKIMG));
	CLoadImgDlg dlg(true, nullptr, nullptr,
	                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_EXPLORER,
	                strFilterIMG, m_pScreen->GetBackgroundWindow());
	dlg.GetOFN().lpstrInitialDir = g_Config.m_strIMGPath; // диалог всегда будем начинать с домашней директории образов.

	if (dlg.DoModal() == IDOK)
	{
		CString str = dlg.GetPathName();
		//тут надо примонтировать str в заданный привод nDrive
		g_Config.SetDriveImgName(nDrive, str);

		if (m_pChip)
		{
			m_pChip->GetFDD()->AttachImage(nDrive, str);
		}
	}

	//nBtnID - ид кнопки, нужно заменить картинку, на картинку со значком.
	ChangeImageIcon(nBtnID, nDrive);
	SetFocusToBK();
}

void CMainFrame::StopAll()
{
	StopTimer();

	if (m_pChip)
	{
		m_pChip->StopCPU(); //остановка CPU - там прекразается вызов фреймов и поток работает вхолостую
		m_pChip->StopTimerThread(); //остановка и завершение потока.
	}
}

void CMainFrame::StartAll()
{
	if (m_pChip)
	{
		m_pChip->StartTimerThread();
		m_pChip->RunCPU();
	}

	StartTimer();
}

void CMainFrame::SetFocusToBK()
{
	auto vw = GetActiveView();

	if (vw)
	{
		vw->SetFocus();
	}
	else
	{
		TRACE("Nope Active Views. Focus set to main Window!\n");
		AfxGetMainWnd()->SetFocus();
	}
}


void CMainFrame::SetFocusToDebug()
{
	m_paneDisassembleView.SetFocus();
}

LRESULT CMainFrame::OnCpuBreak(WPARAM wParam, LPARAM lParam)
{
	if (m_paneDisassembleView.IsVisible())
	{
		if (m_pChip)
		{
			register uint16_t pc = m_pChip->GetRON(CCPU::R_PC);
			//прорисовываем окно дизассемблера
			m_pDebugger->SetCurrentAddress(pc);
			//обновим адрес в поле адреса.
			m_paneDisassembleView.UpdateDebugAddress();
		}
	}

	SetDebugCtrlsState();
	return S_OK;
}

void CMainFrame::OnFileLoadstate()
{
	CString strFilterMSF(MAKEINTRESOURCE(IDS_FILEFILTER_MSF));
	CLoadMemoryDlg dlg(true, nullptr, nullptr,
	                   OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_EXPLORER,
	                   strFilterMSF, m_pScreen->GetBackgroundWindow());
	dlg.GetOFN().lpstrInitialDir = g_Config.m_strMemPath;

	if (dlg.DoModal() == IDOK)
	{
		LoadMemoryState(dlg.GetPathName());
	}

	SetFocusToBK();
}

void CMainFrame::OnFileSavestate()
{
	CString strFilterMSF(MAKEINTRESOURCE(IDS_FILEFILTER_MSF));
	CFileDialog dlg(false, _T("msf"), nullptr,
	                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
	                strFilterMSF, m_pScreen->GetBackgroundWindow());
	dlg.GetOFN().lpstrInitialDir = g_Config.m_strMemPath;

	if (dlg.DoModal() == IDOK)
	{
		if (SaveMemoryState(dlg.GetPathName()))
		{
		}
	}

	g_Config.m_strMemPath = g_Config.GetFilePath(dlg.GetPathName());
	SetFocusToBK();
}

void CMainFrame::OnFileLoadtape()
{
	CString strFilterTape(MAKEINTRESOURCE(IDS_FILEFILTER_TAPE_LOAD));
	CString strTapeExt(MAKEINTRESOURCE(IDS_FILEEXT_TAPE));
	CLoadTapeDlg dlg(true, strTapeExt, nullptr,
	                 OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR,
	                 strFilterTape, m_pScreen->GetBackgroundWindow());
	dlg.GetOFN().lpstrInitialDir = g_Config.m_strTapePath;

	if (dlg.DoModal() == IDOK)
	{
		StartPlayTape(dlg.GetPathName());
	}

	SetFocusToBK();
}

void CMainFrame::OnUpdateFileLoadtape(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!g_Config.m_bEmulateLoadTape);
}


void CMainFrame::OnFileScreenshot()
{
	MakeScreenShot();
}


void CMainFrame::OnCpuLongReset()
{
	m_bLongResetPress = true;
	OnCpuResetCpu();
}

void CMainFrame::OnCpuSuResetCpu()
{
	bool bSU = m_paneBKVKBDView.isSUPress();
	m_paneBKVKBDView.SetSUStatus(true);
	OnCpuResetCpu();
	m_paneBKVKBDView.SetSUStatus(bSU);
}

void CMainFrame::OnCpuResetCpu()
{
	if (!m_pChip)
	{
		return;
	}

	if (m_pChip->IsCPURun()) //защита от множественного вызова функции.
	{
		m_pChip->StopCPU();
		BKFDD_MODEL fdd_model = m_pChip->GetFDDType();

		//если контроллер А16М и сделали длинный ресет или контроллер СМК512 - делаем перезапуск
		//с адреса, который задаётся контроллером.
		if (
		    ((fdd_model == DEV_FDD_A16M) && m_bLongResetPress) ||
		    (fdd_model == DEV_FDD_SMK512)
		)
		{
			m_pChip->SetAltProMode(ALTPRO_A16M_START_MODE);
			m_pChip->SetAltProCode(0);
			m_pChip->ResetCold(0);
		}
		else
		{
			//если у нас БК11, то можно реализовать СУ/ресет - перезапуск по 100000
			if ((m_pChip->GetBoardModel() != BK_0010) && m_paneBKVKBDView.isSUPress())
			{
				m_pChip->ResetCold(040000);
			}
			else
			{
				m_pChip->ResetCold(0);
			}

			//СУ/ресет не реализовывается на контроллере СМК512 потому, что всегда подменяется
			//содержимое 177716 по чтению наложением ПЗУ.
			//А на А16М при коротком ресете и на обычных КНГМД СУ/ресет работает
		}

		m_bLongResetPress = false;
		InitKbdStatus(); //переинициализируем состояния клавиатуры
		// Запускаем CPU
		m_pChip->RunCPU();
	}

	//если установлен флаг остановки после создания, приостановим выполнение
	if (g_Config.m_bPauseCPUAfterStart)
	{
		m_pChip->BreakCPU();
	}
}

void CMainFrame::OnCpuRunbk001001()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0010_01);
}

void CMainFrame::OnCpuRunbk001001Focal()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0010_01_MSTD);
}

void CMainFrame::OnCpuRunbk00100132k()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0010_01_EXT32RAM);
}

void CMainFrame::OnCpuRunbk001001Fdd()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0010_01_FDD);
}

void CMainFrame::OnCpuRunbk001001Fdd16k()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0010_01_A16M);
}

void CMainFrame::OnCpuRunbk001001FddSmk512()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0010_01_SMK512);
}

void CMainFrame::OnCpuRunbk001001FddSamara()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0010_01_SAMARA);
}

void CMainFrame::OnCpuRunbk0011()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011);
}

void CMainFrame::OnCpuRunbk0011Fdd()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011_FDD);
}

void CMainFrame::OnCpuRunbk0011FddA16m()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011_A16M);
}

void CMainFrame::OnCpuRunbk0011FddSmk512()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011_SMK512);
}

void CMainFrame::OnCpuRunbk0011FddSamara()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011_SAMARA);
}

void CMainFrame::OnCpuRunbk0011m()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011M);
}

void CMainFrame::OnCpuRunbk0011mFDD()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011M_FDD);
}

void CMainFrame::OnCpuRunbk0011mFddA16m()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011M_A16M);
}

void CMainFrame::OnCpuRunbk0011mFddSmk512()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011M_SMK512);
}

void CMainFrame::OnCpuRunbk0011mFddSamara()
{
	SetupConfiguration(CONFIG_BKMODEL_NUMBER_BK_0011M_SAMARA);
}

void CMainFrame::OnUpdateCpuRunbk001001(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0010_01);
}

void CMainFrame::OnUpdateCpuRunbk001001Focal(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0010_01_MSTD);
}

void CMainFrame::OnUpdateCpuRunbk00100132k(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0010_01_EXT32RAM);
}

void CMainFrame::OnUpdateCpuRunbk001001Fdd(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0010_01_FDD);
}

void CMainFrame::OnUpdateCpuRunbk001001Fdd16k(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0010_01_A16M);
}

void CMainFrame::OnUpdateCpuRunbk001001FddSmk512(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0010_01_SMK512);
}

void CMainFrame::OnUpdateCpuRunbk001001FddSamara(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0010_01_SAMARA);
}

void CMainFrame::OnUpdateCpuRunbk0011(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011);
}

void CMainFrame::OnUpdateCpuRunbk0011Fdd(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011_FDD);
}


void CMainFrame::OnUpdateCpuRunbk0011FddA16m(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011_A16M);
}

void CMainFrame::OnUpdateCpuRunbk0011FddSmk512(CCmdUI *pCmdUI)
{
	//на БК11 СМК не работает, т.к. использует п/п ПЗУ БК11М
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011_SMK512);
}

void CMainFrame::OnUpdateCpuRunbk0011FddSamara(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011_SAMARA);
}

void CMainFrame::OnUpdateCpuRunbk0011m(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011M);
}

void CMainFrame::OnUpdateCpuRunbk0011mFDD(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011M_FDD);
}

void CMainFrame::OnUpdateCpuRunbk0011mFddA16m(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011M_A16M);
}

void CMainFrame::OnUpdateCpuRunbk0011mFddSmk512(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011M_SMK512);
}

void CMainFrame::OnUpdateCpuRunbk0011mFddSamara(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio(g_Config.m_BKConfigurationModelNumber == CONFIG_BKMODEL_NUMBER_BK_0011M_SAMARA);
}


void CMainFrame::OnCpuAccelerate()
{
	if (m_pChip)
	{
		m_pChip->AccelerateCPU();
		m_paneRegistryDumpViewCPU.UpdateFreq();
	}
}

void CMainFrame::OnUpdateCpuAccelerate(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->CanAccelerate() : FALSE);
}

void CMainFrame::OnCpuSlowdown()
{
	if (m_pChip)
	{
		m_pChip->SlowdownCPU();
		m_paneRegistryDumpViewCPU.UpdateFreq();
	}
}

void CMainFrame::OnUpdateCpuSlowdown(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->CanSlowDown() : FALSE);
}

void CMainFrame::OnCpuNormalspeed()
{
	if (m_pChip)
	{
		m_pChip->NormalizeCPU();
		m_paneRegistryDumpViewCPU.UpdateFreq();
	}
}



void CMainFrame::OnOptionsEnableSpeaker()
{
	g_Config.m_bSpeaker = !m_speaker.IsSoundEnabled();
	m_speaker.EnableSound(g_Config.m_bSpeaker);
}

void CMainFrame::OnUpdateOptionsEnableSpeaker(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_speaker.IsSoundEnabled());
}

void CMainFrame::OnOptionsEnableCovox()
{
	g_Config.m_bCovox = !m_covox.IsSoundEnabled();
	m_covox.EnableSound(g_Config.m_bCovox);

	if (g_Config.m_bCovox)
	{
		//выключим AY
		g_Config.m_bAY8910 = false;
		m_ay8910.EnableSound(g_Config.m_bAY8910);
	}
}

void CMainFrame::OnUpdateOptionsEnableCovox(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_covox.IsSoundEnabled());
}

void CMainFrame::OnOptionsStereoCovox()
{
	g_Config.m_bStereoCovox = !m_covox.IsStereo();
	m_covox.SetStereo(g_Config.m_bStereoCovox);
}

void CMainFrame::OnUpdateOptionsStereoCovox(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_covox.IsStereo());
}

void CMainFrame::OnOptionsEnableAy8910()
{
	g_Config.m_bAY8910 = !m_ay8910.IsSoundEnabled();
	m_ay8910.EnableSound(g_Config.m_bAY8910);

	if (g_Config.m_bAY8910)
	{
		//выключим ковокс
		g_Config.m_bCovox = false;
		m_covox.EnableSound(g_Config.m_bCovox);
	}
}

void CMainFrame::OnUpdateOptionsEnableAy8910(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_ay8910.IsSoundEnabled());
}

void CMainFrame::OnOptionsSpeakerFilter()
{
	g_Config.m_bSpeakerFilter = !m_speaker.GetFilter();
	m_speaker.SetFilter(g_Config.m_bSpeakerFilter);
}

void CMainFrame::OnUpdateOptionsSpeakerFilter(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_speaker.GetFilter());
}

void CMainFrame::OnOptionsCovoxFilter()
{
	g_Config.m_bCovoxFilter = !m_covox.GetFilter();
	m_covox.SetFilter(g_Config.m_bCovoxFilter);
}

void CMainFrame::OnUpdateOptionsCovoxFilter(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_covox.GetFilter());
}

void CMainFrame::OnOptionsAy8910Filter()
{
	g_Config.m_bAY8910Filter = !m_ay8910.GetFilter();
	m_ay8910.SetFilter(g_Config.m_bAY8910Filter);
}

void CMainFrame::OnUpdateOptionsAy8910Filter(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_ay8910.GetFilter());
}


void CMainFrame::OnOptionsEmulateBkkeyboard()
{
	g_Config.m_bBKKeyboard = !g_Config.m_bBKKeyboard;
}

void CMainFrame::OnUpdateOptionsEmulateBkkeyboard(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bBKKeyboard);
}


void CMainFrame::OnOptionsEnableJoystick()
{
	g_Config.m_bJoystick = !g_Config.m_bJoystick;

	if (g_Config.m_bJoystick) //если включаем джойстик
	{
		g_Config.m_bICLBlock = false; //блок нагрузок выключаем
	}
}

void CMainFrame::OnUpdateOptionsEnableJoystick(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bJoystick);
}

void CMainFrame::OnDebugEnableIclblock()
{
	g_Config.m_bICLBlock = !g_Config.m_bICLBlock;

	if (g_Config.m_bICLBlock) //если включаем блок нагрузок
	{
		g_Config.m_bJoystick = false; //джойстик выключаем
	}
}

void CMainFrame::OnUpdateDebugEnableIclblock(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bICLBlock);
}

void CMainFrame::OnOptionsEmulateFddio()
{
	g_Config.m_bEmulateFDDIO = !g_Config.m_bEmulateFDDIO;
}

void CMainFrame::OnUpdateOptionsEmulateFddio(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bEmulateFDDIO);
}

void CMainFrame::OnOptionsUseSavesdirectory()
{
	g_Config.m_bSavesDefault = !g_Config.m_bSavesDefault;
}

void CMainFrame::OnUpdateOptionsUseSavesdirectory(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bSavesDefault);
	pCmdUI->Enable(g_Config.m_bEmulateSaveTape);
}

void CMainFrame::OnOptionsEmulateTapeLoading()
{
	g_Config.m_bEmulateLoadTape = !g_Config.m_bEmulateLoadTape;
	UpdateTapeDlgControls();
}

void CMainFrame::OnUpdateOptionsEmulateTapeLoading(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bEmulateLoadTape);
}

void CMainFrame::OnOptionsEmulateTapeSaving()
{
	g_Config.m_bEmulateSaveTape = !g_Config.m_bEmulateSaveTape;
	UpdateTapeDlgControls();
}

void CMainFrame::OnUpdateOptionsEmulateTapeSaving(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bEmulateSaveTape);
}

void CMainFrame::OnOptionsTapemanager()
{
	auto pdlg = new CTapeManagerDlg(m_pScreen->GetBackgroundWindow());

	if (pdlg)
	{
		pdlg->DoModal();
		delete pdlg;
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	SetFocusToBK();
}

void CMainFrame::OnAppSettings()
{
	CSettingsDlg settingsDlg;

	if (m_pChip)
	{
		m_pChip->StopCPU(); //остановка CPU, чтобы ничего не попортить
	}

	INT_PTR res = settingsDlg.DoModal();

	if (res == IDOK)
	{
		// перезапускаем конфигурацию.
		CONFIG_BKMODEL_NUMBER n = g_Config.GetBKModelNumber();
		ConfigurationConstructor(n); //конфиг сохраняем там.
	}
	else
	{
		if (m_pChip)
		{
			m_pChip->RunCPU();
		}

		SetFocusToBK();
	}
}

void CMainFrame::OnDebugBreak()
{
	if (m_pChip)
	{
		if (m_pChip->IsCPUBreaked())
		{
			m_pChip->UnbreakCPU(CMotherBoard::ADDRESS_NONE);
			SetFocusToBK();
		}
		else
		{
			m_pChip->BreakCPU();
			SetFocusToDebug();
		}
	}
}

void CMainFrame::OnUpdateDebugBreak(CCmdUI *pCmdUI)
{
	if (m_pChip)
	{
		int nIndex = m_wndToolBarDebug.CommandToIndex(ID_DEBUG_STARTBREAK);
		CMFCToolBarButton *pBtn = m_wndToolBarDebug.GetButton(nIndex);
		CString strMenu;
		int iImage;

		if (m_pChip->IsCPUBreaked())
		{
			strMenu.LoadString(IDS_MENU_DEBUG_CONTINUE);
			iImage = GetCmdMgr()->GetCmdImage(ID_DEBUG_START);
		}
		else
		{
			strMenu.LoadString(IDS_MENU_DEBUG_BREAK);
			iImage = GetCmdMgr()->GetCmdImage(ID_DEBUG_BREAK);
		}

		pBtn->SetImage(iImage);
		pCmdUI->SetText(strMenu);
		m_wndToolBarDebug.UpdateData();
		m_wndToolBarDebug.RedrawWindow();
	}
}

void CMainFrame::OnDebugStepinto()
{
	if (m_pChip)
	{
		m_pChip->RunInto();
	}
}

void CMainFrame::OnUpdateDebugStepinto(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->IsCPUBreaked() : FALSE);
}

void CMainFrame::OnDebugStepover()
{
	if (m_pChip)
	{
		m_pChip->RunOver();
	}
}

void CMainFrame::OnUpdateDebugStepover(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->IsCPUBreaked() : FALSE);
}

void CMainFrame::OnDebugStepout()
{
	if (m_pChip)
	{
		m_pChip->RunOut();
	}
}

void CMainFrame::OnUpdateDebugStepout(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->IsCPUBreaked() : FALSE);
}

void CMainFrame::OnDebugRuntocursor()
{
	if (m_pChip)
	{
		m_pChip->RunToAddr(m_pDebugger->GetCursorAddress());
	}
}

void CMainFrame::OnUpdateDebugRuntocursor(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->IsCPUBreaked() : FALSE);
}

void CMainFrame::OnDebugBreakpoint()
{
	if (!m_pDebugger->SetSimpleBreakpoint())
	{
		m_pDebugger->RemoveBreakpoint();
	}

	m_paneDisassembleView.Invalidate(FALSE);
}

void CMainFrame::OnDebugMemmap()
{
	if (!m_pChip)
	{
		return;
	}

	/*
	TODO доделать:
	1. синхронизацию. Нельзя удалять, пока работает DrawCurrentTab и нельзя его запускать когда объект удаляется.
	Если перед удалением объекта останавливать поток TimerThreadFunc, то конфликтов не будет.
	Удаление никогда не будет работать параллельно с DrawCurrentTab
	2. подумать насчёт переделки в DockingTab. хоть размеры и не способствуют, может будет проще управлять
	этой штукой, если она будет DockingTab с запрещённым докингом.
	*/
	m_bBKMemViewOpen = false;

	if (m_pBKMemView) //если раньше окно уже было открыто, а мы снова пробуем открыть
	{
		m_pBKMemView->DestroyWindow(); //старое окно удалим
	}

	//и пойдём создавать новое окно
	auto pBKMemVw = new CBKMEM_Dlg(m_pChip->GetBoardModel(), m_pChip->GetFDDType(),
	                               m_pChip->GetMainMemory(), m_pChip->GetAddMemory(), this); //обязательно создавать динамически.

	if (pBKMemVw)
	{
		if (pBKMemVw->Create(IDD_BKMEM_MAP_DIALOG, this))
		{
			if (m_rectMemMap != CRect(0, 0, 0, 0))
			{
				pBKMemVw->MoveWindow(m_rectMemMap, false);
			}

			pBKMemVw->ShowWindow(SW_SHOW);
			m_pBKMemView = pBKMemVw;
			m_bBKMemViewOpen = true;
		}
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}
}

//эта функция нужна только для того, чтобы m_pBKMemView присвоить nullptr
//когда мы закрываем карту памяти кнопкой крестик в правом верхнем углу
LRESULT CMainFrame::OnMemMapClose(WPARAM, LPARAM)
{
	m_bBKMemViewOpen = false; //при закрытии крестиком ещё и эту переменную надо разблокировать
	m_pBKMemView = nullptr;
	return S_OK;
}

LRESULT CMainFrame::OnMemDumpUpdate(WPARAM, LPARAM)
{
	m_paneMemoryDumpView.DisplayMemDump();
	return S_OK;
}


void CMainFrame::OnDebugDumpregsInterval(UINT id)
{
	switch (id)
	{
		default:
		case ID_DEBUG_DUMPREGS_INTERVAL_0:
			g_Config.m_nRegistersDumpInterval = 0;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_1:
			g_Config.m_nRegistersDumpInterval = 1;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_2:
			g_Config.m_nRegistersDumpInterval = 2;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_3:
			g_Config.m_nRegistersDumpInterval = 3;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_4:
			g_Config.m_nRegistersDumpInterval = 4;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_5:
			g_Config.m_nRegistersDumpInterval = 5;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_10:
			g_Config.m_nRegistersDumpInterval = 10;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_15:
			g_Config.m_nRegistersDumpInterval = 15;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_20:
			g_Config.m_nRegistersDumpInterval = 20;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_25:
			g_Config.m_nRegistersDumpInterval = 25;
			break;

		case ID_DEBUG_DUMPREGS_INTERVAL_50:
			g_Config.m_nRegistersDumpInterval = 50;
			break;
	}
}

void CMainFrame::OnUpdateDebugDumpregsInterval(CCmdUI *pCmdUI)
{
	switch (g_Config.m_nRegistersDumpInterval)
	{
		default:
			g_Config.m_nRegistersDumpInterval = 0;

		//тут break не нужен! Но и стоять это должно строго перед case 0:
		case 0:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_0);
			break;

		case 1:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_1);
			break;

		case 2:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_2);
			break;

		case 3:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_3);
			break;

		case 4:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_4);
			break;

		case 5:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_5);
			break;

		case 10:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_10);
			break;

		case 15:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_15);
			break;

		case 20:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_20);
			break;

		case 25:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_25);
			break;

		case 50:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_DEBUG_DUMPREGS_INTERVAL_50);
			break;
	}
}

void CMainFrame::OnDebugDialogAskForBreak()
{
	if (m_pChip)
	{
		g_Config.m_bAskForBreak = !g_Config.m_bAskForBreak;
		m_pChip->EnableAskForBreak(g_Config.m_bAskForBreak);
	}
}

void CMainFrame::OnUpdateDebugDialogAskForBreak(CCmdUI *pCmdUI)
{
	if (m_pChip)
	{
		g_Config.m_bAskForBreak = m_pChip->IsAskForBreak();
		pCmdUI->SetCheck(g_Config.m_bAskForBreak);
	}
}

void CMainFrame::OnDebugPauseCpuAfterStart()
{
	g_Config.m_bPauseCPUAfterStart = !g_Config.m_bPauseCPUAfterStart;
}

void CMainFrame::OnUpdateDebugPauseCpuAfterStart(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bPauseCPUAfterStart);
}


void CMainFrame::OnOptionsShowPerformanceOnStatusbar()
{
	g_Config.m_bShowPerformance = !g_Config.m_bShowPerformance;

	if (!g_Config.m_bShowPerformance)
	{
		m_wndStatusBar.SetPaneText(STATUS_PANE_INFO, _T(""));
	}
}

void CMainFrame::OnUpdateOptionsShowPerformanceOnStatusbar(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bShowPerformance);
}


void CMainFrame::OnVkbdtypeKeys(UINT id)
{
	switch (id)
	{
		default:
		case ID_VKBDTYPE_KEYS:
			g_Config.m_nVKBDType = 0;
			m_paneBKVKBDView.SetKeyboardView(IDB_BITMAP_SOFT);
			break;

		case ID_VKBDTYPE_MEMBRANE:
			g_Config.m_nVKBDType = 1;
			m_paneBKVKBDView.SetKeyboardView(IDB_BITMAP_PLEN);
			break;
	}
}

void CMainFrame::OnUpdateVkbdtypeKeys(CCmdUI *pCmdUI)
{
	switch (g_Config.m_nVKBDType)
	{
		default:
			g_Config.m_nVKBDType = 0; //тут break не нужен!

		case 0:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VKBDTYPE_KEYS);
			break;

		case 1:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VKBDTYPE_MEMBRANE);
			break;
	}
}

void CMainFrame::OnViewFullscreenmode()
{
	if (m_pChip)
	{
		m_pChip->StopCPU(false);

		if (m_pScreen->IsFullScreenMode())
		{
			m_pScreen->SetWindowMode();
		}
		else
		{
			m_pScreen->SetFullScreenMode();
		}

		//вышеприведённые функции в процессе работы меняют флаг g_Config.m_bFullscreenMode
		g_Config.m_bFullscreenMode = m_pScreen->IsFullScreenMode(); //поэтому его надо восстановить
		m_pChip->RunCPU(false);
		theApp.GetMainWnd()->ShowWindow(SW_RESTORE); //после выхода из полноэкранного режима окно надо полностью перерисовать
		//а то после вызова диалогов в полноэкранном режиме, при выходе из полноэкранного режима не перерисовывается меню и тулбар
	}
}

void CMainFrame::OnViewColormode()
{
	g_Config.m_bColorMode = !m_pScreen->IsColorMode();
	m_pScreen->SetColorMode(g_Config.m_bColorMode);
}

void CMainFrame::OnUpdateViewColormode(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_pScreen->IsColorMode());
}

void CMainFrame::OnViewAdaptivebwmode()
{
	g_Config.m_bAdaptBWMode = !m_pScreen->IsAdaptMode();
	m_pScreen->SetAdaptMode(g_Config.m_bAdaptBWMode);
}

void CMainFrame::OnUpdateViewAdaptivebwmode(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!m_pScreen->IsColorMode());
	pCmdUI->SetCheck(m_pScreen->IsAdaptMode());
}

void CMainFrame::OnViewLuminoforemode()
{
	g_Config.m_bLuminoforeEmulMode = !m_pScreen->GetLuminoforeEmuMode();
	m_pScreen->SetLuminoforeEmuMode(g_Config.m_bLuminoforeEmulMode);
}

void CMainFrame::OnUpdateViewLuminoforemode(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_pScreen->GetLuminoforeEmuMode());
}

void CMainFrame::OnToolLaunch(UINT id)
{
	LaunchTool(id - ID_TOOL_MENU_0);
}

/*
//!!!
Нужно реализовать:
1. динамическое выпадающее меню. добавлять пункт - "Открыть в БКДЕ" с передачей имени образа в качестве параметра
2. Придумать если возможно - редактирование образа в БКДЕ, и в это время не должно быть конфликтов с эмулятором.
Либо нужно отмонтировать образ на время работы, а при закрытии БКДЕ как-то давать эмулятору знать, что образ
можно назад монтировать.
Либо останавливать эмулятор и не давать к нему доступа пока не будет закрыт БКДЕ.
Оба способа непонятно как сделать.
*/
void CMainFrame::OnFileLoadDrive(UINT id)
{
	int nDrive = -1;

	switch (id)
	{
		case ID_FILE_LOADDRIVE_A:
			nDrive = CFDDController::BK_FDD_DRIVE_A;
			break;

		case ID_FILE_LOADDRIVE_B:
			nDrive = CFDDController::BK_FDD_DRIVE_B;
			break;

		case ID_FILE_LOADDRIVE_C:
			nDrive = CFDDController::BK_FDD_DRIVE_C;
			break;

		case ID_FILE_LOADDRIVE_D:
			nDrive = CFDDController::BK_FDD_DRIVE_D;
			break;

		default:
			return;
	}

	LoadFileImage(id, nDrive);
}

void CMainFrame::OnUpdateFileLoadDrive(CCmdUI *pCmdUI)
{
	int nDrive = -1;

	switch (pCmdUI->m_nID)
	{
		case ID_FILE_LOADDRIVE_A:
			nDrive = CFDDController::BK_FDD_DRIVE_A;
			break;

		case ID_FILE_LOADDRIVE_B:
			nDrive = CFDDController::BK_FDD_DRIVE_B;
			break;

		case ID_FILE_LOADDRIVE_C:
			nDrive = CFDDController::BK_FDD_DRIVE_C;
			break;

		case ID_FILE_LOADDRIVE_D:
			nDrive = CFDDController::BK_FDD_DRIVE_D;
			break;

		default:
			return;
	}

	pCmdUI->Enable((m_pChip) ? m_pChip->GetFDD()->GetDriveState(nDrive) : false);
}

void CMainFrame::OnFileUnmount(UINT id)
{
	int nDrive = -1;
	UINT nIconID = 0;

	switch (id)
	{
		case ID_FILE_UMOUNT_A:
			nDrive = CFDDController::BK_FDD_DRIVE_A;
			nIconID = ID_FILE_LOADDRIVE_A;
			break;

		case ID_FILE_UMOUNT_B:
			nDrive = CFDDController::BK_FDD_DRIVE_B;
			nIconID = ID_FILE_LOADDRIVE_B;
			break;

		case ID_FILE_UMOUNT_C:
			nDrive = CFDDController::BK_FDD_DRIVE_C;
			nIconID = ID_FILE_LOADDRIVE_C;
			break;

		case ID_FILE_UMOUNT_D:
			nDrive = CFDDController::BK_FDD_DRIVE_D;
			nIconID = ID_FILE_LOADDRIVE_D;
			break;

		default:
			return;
	}

	if (m_pChip)
	{
		m_pChip->GetFDD()->DetachImage(nDrive);
		ChangeImageIcon(nIconID, nDrive);
	}

	g_Config.SetDriveImgName(nDrive, g_strEmptyUnit);
}

/*
Как оказалось, единственно возможный способ заменить текст в выпадающем меню -
перехватить событие, и подменять строку при вызове меню.
*/
BOOL CMainFrame::OnShowPopupMenu(CMFCPopupMenu *pMenuPopup)
{
	if (pMenuPopup == nullptr)
	{
		return TRUE;
	}

	CMFCToolBarMenuButton *pParentButton = pMenuPopup->GetParentButton();

	if (pParentButton == nullptr)
	{
		return TRUE;
	}

	int nDrive = -1;

	switch (pParentButton->m_nID)
	{
		case ID_FILE_LOADDRIVE_A:
			nDrive = CFDDController::BK_FDD_DRIVE_A;
			break;

		case ID_FILE_LOADDRIVE_B:
			nDrive = CFDDController::BK_FDD_DRIVE_B;
			break;

		case ID_FILE_LOADDRIVE_C:
			nDrive = CFDDController::BK_FDD_DRIVE_C;
			break;

		case ID_FILE_LOADDRIVE_D:
			nDrive = CFDDController::BK_FDD_DRIVE_D;
			break;
	}

	if (m_pChip && nDrive >= 0)
	{
		//заменяемое значение пункта меню
		CString strn = (m_pChip->GetFDD()->IsAttached(nDrive)) ?
		               g_Config.m_strDrives[nDrive] : g_strEmptyUnit;
		CMFCToolBarMenuButton *item = pMenuPopup->GetMenuItem(pMenuPopup->GetMenuItemCount() - 1);
		item->m_strText = strn;
	}

	return CFrameWndEx::OnShowPopupMenu(pMenuPopup);
}

CPoint CMainFrame::m_aScreenSizes[SCREENSIZE_NUMBER] =
{
	{ -1, -1 }, //0
	{ 256, 192 }, //1
	{ 324, 243 }, //2
	{ 432, 324 }, //3
	{ 512, 384 }, //4
	{ 576, 432 }, //5
	{ 768, 576 }, //6
	{ 1024, 768 } //7
};

void CMainFrame::OnSetScreenSize(UINT id)
{
	switch (id)
	{
		default:
		case ID_VIEW_SCREENSIZE_CUSTOM:
			m_nScreenSize = SCREENSIZE_CUSTOM;
			m_nScreen_X = m_nScreen_CustomX ? m_nScreen_CustomX : m_aScreenSizes[4].x;
			m_nScreen_Y = m_nScreen_CustomY ? m_nScreen_CustomY : m_aScreenSizes[4].y;
			break;

		case ID_VIEW_SCREENSIZE_256X192: //x0.5
			m_nScreenSize = SCREENSIZE_256X192;
			break;

		case ID_VIEW_SCREENSIZE_324X243:
			m_nScreenSize = SCREENSIZE_324X243;
			break;

		case ID_VIEW_SCREENSIZE_432X324:
			m_nScreenSize = SCREENSIZE_432X324;
			break;

		case ID_VIEW_SCREENSIZE_512X384: //x1
			m_nScreenSize = SCREENSIZE_512X384;
			break;

		case ID_VIEW_SCREENSIZE_576X432:
			m_nScreenSize = SCREENSIZE_576X432;
			break;

		case ID_VIEW_SCREENSIZE_768X576: //x1.5
			m_nScreenSize = SCREENSIZE_768X576;
			break;

		case ID_VIEW_SCREENSIZE_1024X768: //x2
			m_nScreenSize = SCREENSIZE_1024X768;
			break;
	}

	if (m_nScreenSize)
	{
		m_nScreen_X = m_aScreenSizes[m_nScreenSize].x;
		m_nScreen_Y = m_aScreenSizes[m_nScreenSize].y;
	}

	CRect rectView; //размеры области CBKView
	CRect rectMain; //размеры главного окна
	GetActiveView()->GetClientRect(&rectView);
	GetWindowRect(rectMain);
	int offsetX = rectView.Width() - m_nScreen_X; //дельта X;
	int offsetY = rectView.Height() - m_nScreen_Y; //дельта Y;
	int newsizeX = rectMain.Width() - offsetX;
	int newsizeY = rectMain.Height() - offsetY;
	SetWindowPos(nullptr, rectMain.left, rectMain.top, newsizeX, newsizeY, SWP_SHOWWINDOW | SWP_NOZORDER);
}


LRESULT CMainFrame::OnScreenSizeChanged(WPARAM wParam, LPARAM lParam)
{
	m_nScreen_X = int(wParam);
	m_nScreen_Y = int(lParam);
	bool bFound = false;

	for (int i = SCREENSIZE_256X192; i < SCREENSIZE_NUMBER; ++i)
	{
		if (m_nScreen_X == m_aScreenSizes[i].x && m_nScreen_Y == m_aScreenSizes[i].y)
		{
			m_nScreenSize = static_cast<ScreenSizeNumber>(i);
			bFound = true;
			break;
		}
	}

	if (!bFound)
	{
		m_nScreen_CustomX = m_nScreen_X;
		m_nScreen_CustomY = m_nScreen_Y;
		m_nScreenSize = SCREENSIZE_CUSTOM;
	}

	return S_OK;
}

void CMainFrame::OnUpdateSetScreenSize(CCmdUI *pCmdUI)
{
	switch (m_nScreenSize)
	{
		default:
			m_nScreenSize = SCREENSIZE_CUSTOM; //тут break не нужен!

		case SCREENSIZE_CUSTOM:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_CUSTOM);
			break;

		case SCREENSIZE_256X192:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_256X192);
			break;

		case SCREENSIZE_324X243:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_324X243);
			break;

		case SCREENSIZE_432X324:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_432X324);
			break;

		case SCREENSIZE_512X384:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_512X384);
			break;

		case SCREENSIZE_576X432:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_576X432);
			break;

		case SCREENSIZE_768X576:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_768X576);
			break;

		case SCREENSIZE_1024X768:
			pCmdUI->SetRadio(pCmdUI->m_nID == ID_VIEW_SCREENSIZE_1024X768);
			break;
	}
}

void CMainFrame::OnOptionsLogAy8910()
{
	if (m_ay8910.isLogEnabled())
	{
		//выключим лог
		m_ay8910.log_done();
	}
	else
	{
		//включим лог
		m_ay8910.log_start();
	}
}


void CMainFrame::OnUpdateOptionsLogAy8910(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_ay8910.isLogEnabled());
}
