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

#include "stdafx.h"
#include "BK.h"
#include "MainFrm.h"
#include "Board.h"
#include "Board_MSTD.h"
#include "Board_EXT32.h"
#include "Board_FDD.h"
#include "Board_11.h"
#include "Board_11_FDD.h"
#include "Board_11M.h"
#include "Board_11M_FDD.h"
#include "Config.h"
#include "Screen.h"
#include "MSFManager.h"
#include "LoadImgDlg.h"
#include "LoadMemoryDlg.h"
#include "LoadTapeDlg.h"
#include "TapeManagerDlg.h"
#include "BKErrMsgSys.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, &CMainFrame::OnViewCustomize)
	ON_REGISTERED_MESSAGE(AFX_WM_CREATETOOLBAR, &CMainFrame::OnToolbarCreateNew)
	ON_REGISTERED_MESSAGE(AFX_WM_RESETTOOLBAR, &CMainFrame::OnToolbarReset)
	ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnApplicationLook)
	ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnUpdateApplicationLook)

	ON_WM_KEYDOWN()
	ON_WM_KEYUP()
	ON_WM_PALETTECHANGED()
	ON_WM_COPYDATA()
	ON_WM_CLOSE()
	ON_WM_TIMER()

	//ON_MESSAGE(WM_DISPLAYCHANGE, &CMainFrame::OnDisplayChange)
	ON_MESSAGE(WM_TAPE_FILE_LOAD, &CMainFrame::OnEmulateTape)
	ON_MESSAGE(WM_CPU_DEBUGBREAK, &CMainFrame::OnCpuBreak)
	ON_MESSAGE(WM_MEMMAP_CLOSE, &CMainFrame::OnMemMapClose)
	ON_MESSAGE(WM_MEMDUMP_NEED_UPDATE, &CMainFrame::OnMemDumpUpdate)
	ON_MESSAGE(WM_DROP_TARGET, &CMainFrame::OnDropFile)
	ON_MESSAGE(WM_VKBD_DOWN, &CMainFrame::OnVirtualKeyDown)
	ON_MESSAGE(WM_VKBD_UP, &CMainFrame::OnVirtualKeyUp)
	//////////////Команды////////////////////
	ON_NOTIFY(NM_CUSTOMDRAW, ID_OPTIONS_SOUND_VOLUME, &CMainFrame::OnLVolumeSlider) // Momental Change volume
	//файл
	ON_COMMAND(ID_FILE_LOADSTATE, &CMainFrame::OnFileLoadstate)
	ON_COMMAND(ID_FILE_SAVESTATE, &CMainFrame::OnFileSavestate)
	ON_COMMAND(ID_FILE_LOADTAPE, &CMainFrame::OnFileLoadtape)
	ON_COMMAND(ID_FILE_SCREENSHOT, &CMainFrame::OnFileScreenshot)
	ON_COMMAND(ID_FILE_LOADDRIVE_A, &CMainFrame::OnFileLoadDriveA)
	ON_COMMAND(ID_FILE_LOADDRIVE_B, &CMainFrame::OnFileLoadDriveB)
	ON_COMMAND(ID_FILE_LOADDRIVE_C, &CMainFrame::OnFileLoadDriveC)
	ON_COMMAND(ID_FILE_LOADDRIVE_D, &CMainFrame::OnFileLoadDriveD)
	//Конфигурация
	ON_COMMAND(ID_CPU_RESETCPU, &CMainFrame::OnCpuResetCpu)
	ON_COMMAND(ID_CPU_LONGRESET, &CMainFrame::OnCpuLongReset)
	ON_COMMAND(ID_CPU_RUNBK0010, &CMainFrame::OnCpuRunbk001001)
	ON_COMMAND(ID_CPU_RUNBK001001_FOCAL, &CMainFrame::OnCpuRunbk001001Focal)
	ON_COMMAND(ID_CPU_RUNBK001001_32K, &CMainFrame::OnCpuRunbk00100132k)
	ON_COMMAND(ID_CPU_RUNBK001001_FDD, &CMainFrame::OnCpuRunbk001001Fdd)
	ON_COMMAND(ID_CPU_RUNBK001001_FDD16K, &CMainFrame::OnCpuRunbk001001Fdd16k)
	ON_COMMAND(ID_CPU_RUNBK001001_FDD_SMK512, &CMainFrame::OnCpuRunbk001001FddSmk512)
	ON_COMMAND(ID_CPU_RUNBK0011M, &CMainFrame::OnCpuRunbk0011m)
	ON_COMMAND(ID_CPU_RUNBK0011M_FDD, &CMainFrame::OnCpuRunbk0011mFDD)
	ON_COMMAND(ID_CPU_RUNBK0011M_FDD_A16M, &CMainFrame::OnCpuRunbk0011mFddA16m)
	ON_COMMAND(ID_CPU_RUNBK0011M_FDD_SMK512, &CMainFrame::OnCpuRunbk0011mFddSmk512)
	ON_COMMAND(ID_CPU_RUNBK0011, &CMainFrame::OnCpuRunbk0011)
	ON_COMMAND(ID_CPU_RUNBK0011_FDD, &CMainFrame::OnCpuRunbk0011Fdd)
	ON_COMMAND(ID_CPU_RUNBK0011_FDD_SMK512, &CMainFrame::OnCpuRunbk0011FddSmk512)
	ON_COMMAND(ID_CPU_ACCELERATE, &CMainFrame::OnCpuAccelerate)
	ON_COMMAND(ID_CPU_ACCELERATESMALL, &CMainFrame::OnCpuAccelerateSmall)
	ON_COMMAND(ID_CPU_SLOWDOWN, &CMainFrame::OnCpuSlowdown)
	ON_COMMAND(ID_CPU_SLOWDOWNSMALL, &CMainFrame::OnCpuSlowdownSmall)
	ON_COMMAND(ID_CPU_NORMALSPEED, &CMainFrame::OnCpuNormalspeed)
	//Опции
	ON_COMMAND(ID_OPTIONS_ENABLE_SPEAKER, &CMainFrame::OnOptionsEnableSpeaker)
	ON_COMMAND(ID_OPTIONS_ENABLE_COVOX, &CMainFrame::OnOptionsEnableCovox)
	ON_COMMAND(ID_OPTIONS_STEREO_COVOX, &CMainFrame::OnOptionsStereoCovox)
	ON_COMMAND(ID_OPTIONS_ENABLE_AY8910, &CMainFrame::OnOptionsEnableAy8910)
	ON_COMMAND(ID_OPTIONS_SPEAKER_FILTHER, &CMainFrame::OnOptionsSpeakerFilter)
	ON_COMMAND(ID_OPTIONS_COVOX_FILTHER, &CMainFrame::OnOptionsCovoxFilter)
	ON_COMMAND(ID_OPTIONS_AY8910_FILTHER, &CMainFrame::OnOptionsAy8910Filter)
	ON_COMMAND(ID_OPTIONS_EMULATE_BKKEYBOARD, &CMainFrame::OnOptionsEmulateBkkeyboard)
	ON_COMMAND(ID_OPTIONS_ENABLE_JOYSTICK, &CMainFrame::OnOptionsEnableJoystick)
	ON_COMMAND(ID_OPTIONS_USE_SAVESDIRECTORY, &CMainFrame::OnOptionsUseSavesdirectory)
	ON_COMMAND(ID_OPTIONS_EMULATE_TAPE_LOADING, &CMainFrame::OnOptionsEmulateTapeLoading)
	ON_COMMAND(ID_OPTIONS_EMULATE_TAPE_SAVING, &CMainFrame::OnOptionsEmulateTapeSaving)
	ON_COMMAND(ID_OPTIONS_TAPEMANAGER, &CMainFrame::OnOptionsTapemanager)
	ON_COMMAND(ID_OPTIONS_SHOWPERFORMANCEONSTATUSBAR, &CMainFrame::OnOptionsShowPerformanceOnStatusbar)
	ON_COMMAND(ID_OPTIONS_EMULATE_FDDIO, &CMainFrame::OnOptionsEmulateFddio)
	//Отладка
	ON_COMMAND(ID_DEBUG_STARTBREAK, &CMainFrame::OnDebugBreak)
	ON_COMMAND(ID_DEBUG_STEPINTO, &CMainFrame::OnDebugStepinto)
	ON_COMMAND(ID_DEBUG_STEPOVER, &CMainFrame::OnDebugStepover)
	ON_COMMAND(ID_DEBUG_STEPOUT, &CMainFrame::OnDebugStepout)
	ON_COMMAND(ID_DEBUG_RUNTOCURSOR, &CMainFrame::OnDebugRuntocursor)
	ON_COMMAND(ID_DEBUG_BREAKPOINT, &CMainFrame::OnDebugBreakpoint)
	ON_COMMAND(ID_DEBUG_DIALOG_ASK_FOR_BREAK, &CMainFrame::OnDebugDialogAskForBreak)
	ON_COMMAND(ID_DEBUG_PAUSE_CPU_AFTER_START, &CMainFrame::OnDebugPauseCpuAfterStart)
	ON_COMMAND_RANGE(ID_DEBUG_DUMPREGS_INTERVAL_0, ID_DEBUG_DUMPREGS_INTERVAL_50, &CMainFrame::OnDebugDumpregsInterval)
	ON_COMMAND(ID_DEBUG_MEMMAP, &CMainFrame::OnDebugMemmap)
	//Вид
	ON_COMMAND_RANGE(ID_VKBDTYPE_KEYS, ID_VKBDTYPE_MEMBRANE, &CMainFrame::OnVkbdtypeKeys)
	ON_COMMAND(ID_VIEW_COLORMODE, &CMainFrame::OnViewColormode)
	ON_COMMAND(ID_VIEW_FULLSCREENMODE, &CMainFrame::OnViewFullscreenmode)
	ON_COMMAND(ID_VIEW_ADAPTIVEBWMODE, &CMainFrame::OnViewAdaptivebwmode)
	//Инструменты
	ON_COMMAND_RANGE(ID_TOOL_MENU_0, ID_TOOL_MENU_9, &CMainFrame::OnToolLaunch)
	//////////////Интерфейс меню////////////////////
	//файл
	ON_UPDATE_COMMAND_UI(ID_FILE_LOADTAPE, &CMainFrame::OnUpdateFileLoadtape)
	ON_UPDATE_COMMAND_UI(ID_FILE_LOADDRIVE_A, &CMainFrame::OnUpdateFileLoadDriveA)
	ON_UPDATE_COMMAND_UI(ID_FILE_LOADDRIVE_B, &CMainFrame::OnUpdateFileLoadDriveB)
	ON_UPDATE_COMMAND_UI(ID_FILE_LOADDRIVE_C, &CMainFrame::OnUpdateFileLoadDriveC)
	ON_UPDATE_COMMAND_UI(ID_FILE_LOADDRIVE_D, &CMainFrame::OnUpdateFileLoadDriveD)
	//Конфигурация
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0010, &CMainFrame::OnUpdateCpuRunbk001001)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FOCAL, &CMainFrame::OnUpdateCpuRunbk001001Focal)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_32K, &CMainFrame::OnUpdateCpuRunbk00100132k)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FDD, &CMainFrame::OnUpdateCpuRunbk001001Fdd)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FDD16K, &CMainFrame::OnUpdateCpuRunbk001001Fdd16k)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK001001_FDD_SMK512, &CMainFrame::OnUpdateCpuRunbk001001FddSmk512)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M, &CMainFrame::OnUpdateCpuRunbk0011m)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M_FDD, &CMainFrame::OnUpdateCpuRunbk0011mFDD)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M_FDD_A16M, &CMainFrame::OnUpdateCpuRunbk0011mFddA16m)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011M_FDD_SMK512, &CMainFrame::OnUpdateCpuRunbk0011mFddSmk512)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011, &CMainFrame::OnUpdateCpuRunbk0011)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011_FDD, &CMainFrame::OnUpdateCpuRunbk0011Fdd)
	ON_UPDATE_COMMAND_UI(ID_CPU_RUNBK0011_FDD_SMK512, &CMainFrame::OnUpdateCpuRunbk0011FddSmk512)
	ON_UPDATE_COMMAND_UI(ID_CPU_ACCELERATE, &CMainFrame::OnUpdateCpuAccelerate)
	ON_UPDATE_COMMAND_UI(ID_CPU_ACCELERATESMALL, &CMainFrame::OnUpdateCpuAccelerate)
	ON_UPDATE_COMMAND_UI(ID_CPU_SLOWDOWN, &CMainFrame::OnUpdateCpuSlowdown)
	ON_UPDATE_COMMAND_UI(ID_CPU_SLOWDOWNSMALL, &CMainFrame::OnUpdateCpuSlowdown)
	//Опции
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_SPEAKER, &CMainFrame::OnUpdateOptionsEnableSpeaker)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_COVOX, &CMainFrame::OnUpdateOptionsEnableCovox)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_STEREO_COVOX, &CMainFrame::OnUpdateOptionsStereoCovox)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_AY8910, &CMainFrame::OnUpdateOptionsEnableAy8910)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_SPEAKER_FILTHER, &CMainFrame::OnUpdateOptionsSpeakerFilter)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_COVOX_FILTHER, &CMainFrame::OnUpdateOptionsCovoxFilter)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_AY8910_FILTHER, &CMainFrame::OnUpdateOptionsAy8910Filter)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_BKKEYBOARD, &CMainFrame::OnUpdateOptionsEmulateBkkeyboard)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_ENABLE_JOYSTICK, &CMainFrame::OnUpdateOptionsEnableJoystick)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_USE_SAVESDIRECTORY, &CMainFrame::OnUpdateOptionsUseSavesdirectory)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_TAPE_LOADING, &CMainFrame::OnUpdateOptionsEmulateTapeLoading)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_TAPE_SAVING, &CMainFrame::OnUpdateOptionsEmulateTapeSaving)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWPERFORMANCEONSTATUSBAR, &CMainFrame::OnUpdateOptionsShowPerformanceOnStatusbar)
	ON_UPDATE_COMMAND_UI(ID_OPTIONS_EMULATE_FDDIO, &CMainFrame::OnUpdateOptionsEmulateFddio)
	//Отладка
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STARTBREAK, &CMainFrame::OnUpdateDebugBreak)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPINTO, &CMainFrame::OnUpdateDebugStepinto)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPOVER, &CMainFrame::OnUpdateDebugStepover)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPOUT, &CMainFrame::OnUpdateDebugStepout)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_RUNTOCURSOR, &CMainFrame::OnUpdateDebugRuntocursor)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_DIALOG_ASK_FOR_BREAK, &CMainFrame::OnUpdateDebugDialogAskForBreak)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_PAUSE_CPU_AFTER_START, &CMainFrame::OnUpdateDebugPauseCpuAfterStart)
	ON_UPDATE_COMMAND_UI_RANGE(ID_DEBUG_DUMPREGS_INTERVAL_0, ID_DEBUG_DUMPREGS_INTERVAL_50, &CMainFrame::OnUpdateDebugDumpregsInterval)
	//Вид
	ON_UPDATE_COMMAND_UI_RANGE(ID_VKBDTYPE_KEYS, ID_VKBDTYPE_MEMBRANE, &CMainFrame::OnUpdateVkbdtypeKeys)
	ON_UPDATE_COMMAND_UI(ID_VIEW_COLORMODE, &CMainFrame::OnUpdateViewColormode)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ADAPTIVEBWMODE, &CMainFrame::OnUpdateViewAdaptivebwmode)

	ON_COMMAND(ID_FILE_UMOUNT_A, &CMainFrame::OnFileUmountA)
	ON_COMMAND(ID_FILE_UMOUNT_B, &CMainFrame::OnFileUmountB)
	ON_COMMAND(ID_FILE_UMOUNT_C, &CMainFrame::OnFileUmountC)
	ON_COMMAND(ID_FILE_UMOUNT_D, &CMainFrame::OnFileUmountD)

	ON_WM_SYSKEYDOWN()
	ON_WM_SYSKEYUP()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	ON_COMMAND(ID_CPU_SURESETCPU, &CMainFrame::OnCpuSuResetCpu)
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(NULL)
	, m_pChip(NULL)
	, m_pSound(NULL)
	, m_pDebugger(NULL)
	, m_pScreen(NULL)
	, m_rectMemMap(CRect(0, 0, 0, 0))
	, m_nSecondsCounter(0)
	, m_nRegsDumpCounter(0)
	, m_nBKConfModel(MSF_CONF_BK1001)
	, m_bIsKeyPress(FALSE)
	, m_bLongResetPress(FALSE)
{
	theApp.m_nAppLook = theApp.GetInt(_T("ApplicationLook"), ID_VIEW_APPLOOK_VS_2008);
	m_nTickCounter = GetTickCount();
	//регистрируем наше глобальное сообщение
	m_nInterAppGlobalMsg = RegisterWindowMessage(_T("Inter BKEmu Application Global Message"));
}

CMainFrame::~CMainFrame()
{
	m_wndToolBarSound.CleanUpImages();
	m_wndToolBarDebug.CleanUpImages();
	SAFE_DELETE(m_pChip);
	SAFE_DELETE(m_pSound);
	m_mtInstance.Unlock();
}

void CMainFrame::MakeTitleVersion()
{
	DWORD dwHandle;
	TCHAR name[MAX_PATH];
	StringCchCopy(name, MAX_PATH, theApp.m_pszExeName);
	StringCchCat(name, MAX_PATH, _T(".exe"));
	theApp.m_strProgramName = CString(name);
	DWORD dwSize = GetFileVersionInfoSize(name, &dwHandle);

	if (dwSize)
	{
		TCHAR *lpData = new TCHAR[ dwSize ];

		if (lpData)
		{
			GetFileVersionInfo(name, dwHandle, dwSize, lpData);
			UINT uBufLen;
			VS_FIXEDFILEINFO *lpfi;
			VerQueryValue(lpData, _T("\\"), (void **)&lpfi, &uBufLen);
			CString strPlatform = (sizeof(LPVOID) == 4) ? _T("x86") : _T("x64");
			CString s, t;
			t.LoadString(IDS_EMUL_TITLE);
			s.Format(_T(" v%d.%d.%d.%d "),
			         HIWORD(lpfi->dwFileVersionMS), LOWORD(lpfi->dwFileVersionMS),
			         HIWORD(lpfi->dwFileVersionLS), LOWORD(lpfi->dwFileVersionLS));
			theApp.m_strProgramTitleVersion = t + s + strPlatform;
			delete[] lpData;
		}
		else
		{
			CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
		}
	}
}


void CMainFrame::MakeWindowTitle()
{
	int n = g_Config.GetBKModelNumber();
	CString strTitle = g_mstrConfigBKModelParameters[n].strBKModelName + _T(" - ") + theApp.m_strProgramTitleVersion;
	SetWindowText(strTitle);
}

static HHOOK g_hHook = NULL; //handle to the hook procedure
static BOOL g_bAppFocused = FALSE; //флаг что фокус на окне, чтобы если окно не в фокусе, кнопки WIN не перехватывались
static CMainFrame *g_pthisObj = NULL; //Указатель на объект this, чтобы можно было вызвать нестатические функции из статической
LRESULT CALLBACK CMainFrame::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	KBDLLHOOKSTRUCT *lp = (KBDLLHOOKSTRUCT *)lParam;

	if (nCode < 0)
	{
		return CallNextHookEx(g_hHook, nCode, wParam, lParam);
	}

	if (g_bAppFocused)
	{
		if (nCode == HC_ACTION)
		{
			switch (wParam)
			{
				case WM_KEYDOWN:
				case WM_SYSKEYDOWN:
					if (lp->vkCode == VK_LWIN || lp->vkCode == VK_RWIN)
					{
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_TAB) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+ТАБ
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+КТ
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0))
					{
						//СУ+КТ
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_F4) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+F4
						g_pthisObj->EmulateKeyDown(lp->vkCode, lp->flags);
						return 1;
					}

					break;

				case WM_KEYUP:
				case WM_SYSKEYUP:
					if (lp->vkCode == VK_LWIN || lp->vkCode == VK_RWIN)
					{
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_TAB) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+ТАБ
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+КТ
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0))
					{
						//СУ+КТ
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					if ((lp->vkCode == VK_F4) && ((lp->flags & LLKHF_ALTDOWN) != 0))
					{
						//АР2+F4
						g_pthisObj->EmulateKeyUp(lp->vkCode, lp->flags);
						return 1;
					}

					break;
			}
		}
	}

	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

void CMainFrame::OnSetFocus(CWnd *pOldWnd)
{
	CFrameWndEx::OnSetFocus(pOldWnd);
	g_bAppFocused = TRUE;
}

void CMainFrame::OnKillFocus(CWnd *pNewWnd)
{
	CFrameWndEx::OnKillFocus(pNewWnd);
	g_bAppFocused = FALSE;
}

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

	//ставим хук для перехвата кнопок левый, правый WIN и прочие системные кнопки
	g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)&LowLevelKeyboardProc, NULL /*GetModuleHandle(NULL)*/, 0);
	ASSERT(g_hHook != NULL);
	g_pthisObj = this;
	MakeTitleVersion();
	BOOL bNameValid;

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

	// включить быструю (Alt+перетаскивание) настройку панелей инструментов
	CMFCToolBar::EnableQuickCustomization();
	/*
	IDR_MENU_IMAGES_256 - ресурс тулбара с картинками, который добавляется в коллекцию картинок, для того
	чтобы можно было по ID картинок данного тулбара менять картинки на кнопках видимых тулбаров.
	сам CMFCToolBar на основе данного тулбара не создаётся, У каждой картинки должен быть свой, уникальный ID
	*/
	CMFCToolBar::AddToolBarForImageCollection(theApp.m_bHiColorIcons ? IDR_MENU_IMAGES_256 : IDR_MENU_IMAGES);
	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 ? IDR_MAINFRAME_256 : IDR_MAINFRAME))
	{
		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 ? IDR_SOUNDTOOLBAR_256 : IDR_SOUNDTOOLBAR))
	{
		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 ? IDR_DEBUGTOOLBAR_256 : IDR_DEBUGTOOLBAR))
	{
		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(NULL, uiFirstUserToolBarId, uiLastUserToolBarId);

	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, 20);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_KBD_CAPS, 28);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_KBD_AR2, 20);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_KBD_SU, 20);
	m_wndStatusBar.SetPaneWidth(STATUS_PANE_INFO, 352);
	OutKeyboardSstatus(STATUS_PANE_KBD_XLAT);
	m_BKVKBDView.SetCapitalStatus(GetKeyState(VK_CAPITAL) & 1); //зафиксируем текущее состояние капслока
	OutKeyboardSstatus(STATUS_PANE_KBD_CAPS);
	OutKeyboardSstatus(STATUS_PANE_KBD_AR2);
	OutKeyboardSstatus(STATUS_PANE_KBD_SU);
	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_DisassembleView.EnableDocking(CBRS_ALIGN_ANY);
	m_memoryDumpView.EnableDocking(CBRS_ALIGN_ANY);
	DockPane(&m_DisassembleView);
	m_memoryDumpView.DockToWindow(&m_DisassembleView, CBRS_ALIGN_BOTTOM); //для этого панели m_DisassembleView и m_memoryDumpView обязательно должны быть видимые.
	//иначе, m_memoryDumpView не может придокиться к m_DisassembleView и остаётся висеть в воздухе, так что даже перемещать её нельзя.
	m_OscillatorView.EnableDocking(CBRS_ALIGN_ANY);
	m_BKVKBDView.EnableDocking(CBRS_ALIGN_ANY);
	DockPane(&m_OscillatorView);
	DockPane(&m_BKVKBDView);
	CDockablePane *pTabbedBar = NULL;
	m_BKVKBDView.AttachToTabWnd(&m_OscillatorView, DM_SHOW, TRUE, &pTabbedBar);
	m_registryDumpView.EnableDocking(CBRS_ALIGN_ANY);
	m_tapeCtrlView.EnableDocking(CBRS_ALIGN_ANY);
	DockPane(&m_registryDumpView);
	m_tapeCtrlView.DockToWindow(&m_registryDumpView, CBRS_ALIGN_BOTTOM);
	// установите наглядный диспетчер и стиль на основе постоянного значения
	OnApplicationLook(theApp.m_nAppLook);
	m_registryDumpView.AttachDebugger(m_pDebugger);
	m_memoryDumpView.AttachDebugger(m_pDebugger);
	m_DisassembleView.AttachDebugger(m_pDebugger);

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

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

	m_dropTarget.Register(this);
	// Включить функцию замены меню панелей инструментов и закрепляемых окон
	EnablePaneMenu(TRUE, ID_VIEW_CUSTOMIZE, strCustomize, ID_VIEW_TOOLBAR);
	// Инициализация звуковой подсистемы
	m_pSound = new CBkSound(this);  //нужно обязательно перед установкой текущей громкости
	InitRegistry(); //регистрируем типы и расширения, чтоб по клику на файлах прога запускалась
	// Инициализация эмулятора
	CString strLine = GetCommandLine();

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

	InitEmulator(); //Читаем из ини файла параметры
	return 0;
}

LRESULT CMainFrame::OnToolbarReset(WPARAM wp, LPARAM)
{
	UINT uiToolBarId = (UINT) wp;

	if (uiToolBarId == (theApp.m_bHiColorIcons ? IDR_SOUNDTOOLBAR_256 : IDR_SOUNDTOOLBAR))
	{
		CSliderButton btnSlider(ID_OPTIONS_SOUND_VOLUME, -1, TBS_TRANSPARENTBKGND);
		btnSlider.SetRange(0, 0xffff);
		m_wndToolBarSound.ReplaceButton(ID_OPTIONS_SOUND_VOLUME, btnSlider);
	}
	else if (uiToolBarId == (theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME))
	{
		//создаём кнопки с выпадающими меню. 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);
	}

	return S_OK;
}


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

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

	*pResult = S_OK;
}

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

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

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

	if (!m_tapeCtrlView.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_DisassembleView.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_memoryDumpView.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_OscillatorView.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_BKVKBDView.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)
{
	//!!! тут надо бы свои иконки для окон присобачить
	HICON hIcon = (HICON) ::LoadImage(::AfxGetResourceHandle(), MAKEINTRESOURCE(bHiColorIcons ? IDI_DEBUG_WND_HC : IDI_DEBUG_WND), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), 0);
	m_registryDumpView.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_memoryDumpView.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_DisassembleView.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_tapeCtrlView.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_OscillatorView.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_BKVKBDView.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()
{
	CMFCToolBarsCustomizeDialog *pDlgCust = new CMFCToolBarsCustomizeDialog(this, TRUE /* сканировать меню*/);
	pDlgCust->EnableUserDefinedToolbars();
	pDlgCust->Create();
}

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

	if (lres)
	{
		CMFCToolBar *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(NULL, NULL, 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);
}

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

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

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

		if (pUserToolbar != NULL)
		{
			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()));
		return TRUE; // Закроем приложение
	}

	if (ProcessFile(TRUE)) //вот здесь только создаётся борда бкашки
	{
		return TRUE;
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFBKBOARDERROR], MB_OK);
		return FALSE;
	}
}


void CMainFrame::InitEmulator()
{
	//!!!Вся эта фигня со слайдером не работает, потому что текущее положение слайдера сохраняется в реестре,
	// и позже, не здесь, а где-то в другом месте восстанавливается прошлое значение.
	CSliderButton *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); //продублируем для надёжности
	int n = g_Config.GetBKModelNumber();
	m_nBKConfModel = g_mstrConfigBKModelParameters[n].nBKModel;
	g_Config.m_BKFDDModel = g_mstrConfigBKModelParameters[n].nBKFDDModel;
	// инициализация текущих настроек
	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);

	if (g_Config.m_bFullscreenMode)
	{
		m_pScreen->SetFullScreenMode();
	}

	m_tape.SetPlayWavePos(0);
	// Настройка панели управления записью
	UpdateTapeDlgControls();
	m_tapeCtrlView.InitParams(g_Config.m_strTapePath, g_Config.m_bTapeAutoBeginDetection, g_Config.m_bTapeAutoEnsDetection, &m_tape);
	UpdateData(FALSE);
}


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

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

void CMainFrame::RegisterShellCommand(CString &strName, initRegRaram *regParam)
{
	HKEY hKey;
	LONG 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, NULL, 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:
			{
				//основная прога обрабатывает запрос копии
				//если другая копия проги спрашивает эту копию, надо ей ответить, и послать
				//хэндл окна этой копии
				HWND 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;
				strTitle.LoadString(IDS_EMUL_TITLE);
				CString strPrompt;
				strPrompt.LoadString(IDS_COMMAND_PROMPT_INFO);
				CBKMessageBox(strTitle + strPrompt, MB_OK);
				return TRUE;
			}

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

	return TRUE;
}


void CMainFrame::StartPlayTape(CString &strPath)
{
	CString strWaveExt;
	strWaveExt.LoadString(IDS_FILEEXT_WAVE);
	CString strTapeExt;
	strTapeExt.LoadString(IDS_FILEEXT_TAPE);
	CString strTapePath = strPath;
	CString strExt = g_Config.GetFileExt(strTapePath);

	if (!strExt.CompareNoCase(strWaveExt))
	{
		m_tape.LoadWaveFile(strTapePath);
	}
	else
	{
		m_tape.LoadMSFFile(strTapePath);
	}

	TAPE_FILE_INFO tfi;
	m_tape.GetWaveFile(&tfi);
	m_tape.SetPlayWavePos(0);
	m_tape.StartPlay();
}


void CMainFrame::UpdateTapeDlgControls()
{
	m_tapeCtrlView.UpdateTapeControls(!g_Config.m_bEmulateSaveTape);
}

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

		case BKTIMER_SET_TITLE:
			// цель этого таймера - однократно сработать, чтобы можно было модифицировать
			// заголовок окна, а то вообще никак при запуске не получается его модифицировать.
			// просто не знаю, куда воткнуть команду инициализации
			KillTimer(BKTIMER_SET_TITLE);
			MakeWindowTitle();
			break;
	}
}

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

	int nTmpTick;

	if ((nTmpTick = GetTickCount()) - m_nTickCounter >= 1000)
	{
		m_nTickCounter = nTmpTick;
		m_nSecondsCounter++;

		if (g_Config.m_bShowPerformance)
		{
			CRect rect;
			m_pScreen->GetClientRect(&rect);
			// Показываем производительность в строке состояния
			CString strPerformance;
			strPerformance.Format(_T("Scr: %d x %d, Uptime %i s., CPU: %d Hz, FPS: %i"), rect.Width(), rect.Height(), m_nSecondsCounter, m_pChip->GetCPUFreq(), m_pScreen->GetFPS());
			m_wndStatusBar.SetPaneText(STATUS_PANE_INFO, strPerformance);
		}
	}

	if (m_pChip->IsCPUBreaked())   //если проц остановлен
	{
		return;    //то информацию не обновлять, т.к. она принудительно обновляется при пошаговой отладке.
	}

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

	if (m_lockBKMemViewOpen.IsLocked())
	{
		m_pBKMemView->DrawCurrentTab();
	}
}


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)
{
	CString *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();
}


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(MSF_CONF_BK1001);    // Создаём конфигурацию по умолчанию
		}
	}
	else
	{
		//иначе, смотрим, что там.
		if (type) //если вызвали из onCreate - то создаём заданную модель
		{
			bRes = ConfigurationConstructor(m_nBKConfModel);  // Создаём заданную конфигурацию
		}
		else
		{
			//если вызвали из ДрагнДропа - то
			// Если задан bin файл
			if (!m_strBinFileName.IsEmpty())
			{
				bRes = ConfigurationConstructor(MSF_CONF_BK1001);    // Создаём конфигурацию по умолчанию
			}

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

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

	return bRes;
}


BOOL CMainFrame::EmulateJoystickDown(UINT nChar, UINT nFlags)
{
	if (m_pChip && g_Config.m_bJoystick)
	{
		if (!(nFlags & KF_EXTENDED))
		{
			WORD joystick = m_pChip->m_reg177714in;

			switch (nChar)
			{
				case VK_HOME:
					joystick |= 0040;   // джойстик: кнопка 1; мышь: левая кнопка
					break;

				case VK_END:
					joystick |= 0200;   // джойстик: кнопка 2;
					break;

				case VK_PRIOR:
					joystick |= 0100;   // джойстик: кнопка 3; мышь: правая кнопка
					break;

				case VK_NEXT:
					joystick |= 0400;   // джойстик: кнопка 4
					break;

				case VK_CLEAR:
					joystick |= 0020;   // джойстик: кнопка 5
					break;

				case VK_RIGHT:
					joystick |= 0002;   // джойстик: вправо; мышь: вправо
					break;

				case VK_LEFT:
					joystick |= 0010;   // джойстик: влево; мышь: влево
					break;

				case VK_UP:
					joystick |= 0001;   // джойстик: вверх; мышь: вверх
					break;

				case VK_DOWN:
					joystick |= 0004;   // джойстик: вниз; мышь: вниз
					break;

				default:
					return FALSE;
			}

			m_pChip->m_reg177714in = joystick;
			return TRUE;
		}
	}

	return FALSE;
}


BOOL CMainFrame::EmulateJoystickUp(UINT nChar, UINT nFlags)
{
	if (m_pChip && g_Config.m_bJoystick)
	{
		if (!(nFlags & KF_EXTENDED))
		{
			WORD joystick = m_pChip->m_reg177714in;

			switch (nChar)
			{
				case VK_HOME:
					joystick &= ~0040;  // джойстик: кнопка 1; мышь: левая кнопка
					break;

				case VK_END:
					joystick &= ~0200;  // джойстик: кнопка 2;
					break;

				case VK_PRIOR:
					joystick &= ~0100;  // джойстик: кнопка 3; мышь: правая кнопка
					break;

				case VK_NEXT:
					joystick &= ~0400;  // джойстик: кнопка 4
					break;

				case VK_CLEAR:
					joystick &= ~0020;  // джойстик: кнопка 5
					break;

				case VK_RIGHT:
					joystick &= ~0002;  // джойстик: вправо; мышь: вправо
					break;

				case VK_LEFT:
					joystick &= ~0010;  // джойстик: влево; мышь: влево
					break;

				case VK_UP:
					joystick &= ~0001;  // джойстик: вверх; мышь: вверх
					break;

				case VK_DOWN:
					joystick &= ~0004;  // джойстик: вниз; мышь: вниз
					break;

				default:
					return FALSE;
			}

			m_pChip->m_reg177714in = joystick;
			return TRUE;
		}
	}

	return FALSE;
}


void CMainFrame::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (nChar == VK_MENU) //если нажат Ctrl, то нажатие alt отлавливается тут и только тут
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		m_BKVKBDView.SetAR2Status(TRUE);
		OutKeyboardSstatus(STATUS_PANE_KBD_AR2);
	}
	else if (m_pChip && !EmulateJoystickDown(nChar, nFlags))
	{
		EmulateKeyDown(nChar, nFlags);
	}
}


void CMainFrame::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (nChar == VK_MENU) //после нажатия alt+клавиша, отжатие alt отлавливается тут и только тут
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		m_BKVKBDView.SetAR2Status(FALSE);
		OutKeyboardSstatus(STATUS_PANE_KBD_AR2);
	}
	else if (m_pChip && !EmulateJoystickUp(nChar, nFlags))
	{
		EmulateKeyUp(nChar, nFlags);
	}
}



void CMainFrame::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// Если нажали любой Alt
	if (nChar == VK_MENU)
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		m_BKVKBDView.SetAR2Status(TRUE);
		OutKeyboardSstatus(STATUS_PANE_KBD_AR2);
	}
	else
	{
		EmulateKeyDown(nChar, nFlags);
	}
}


void CMainFrame::OnSysKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (nChar == VK_MENU) //после нажатия alt, отжатие alt отлавливается тут и только тут, если не нажимали при этом других клавиш
	{
		//WORD uScan = HIWORD(pMsg->lParam) & 0x1FF; //это чтобы различать левый/правый альты, если uScan == 56, то левый альт, иначе - правый.
		m_BKVKBDView.SetAR2Status(FALSE);
		OutKeyboardSstatus(STATUS_PANE_KBD_AR2);
	}
	else
	{
		EmulateKeyUp(nChar, nFlags);
	}
}

/*
 *wParam - флаг клавиши BKKeyType
 *Потенциально можно передавать и остальные управляющие клавиши, для отображения статуса.
 *Нужно только подумать, как сделать так, чтобы не конфликтовало с обычной клавиатурой.
 *lParam - если обычная клавиша, то мл.байт - сканкод, ст.байт - прерывание.
 **/
LRESULT CMainFrame::OnVirtualKeyDown(WPARAM wParam, LPARAM lParam)
{
	switch (wParam)
	{
		case BKKeyType::BKKEY_REGULAR:
		{
			BYTE nScanCode = LOBYTE(lParam);
			BYTE nInt = HIBYTE(lParam);

			//если ещё прошлый код не прочитали, новый игнорируем.
			if ((m_pChip->m_reg177660 & 0200) == 0)
			{
				//сюда заходим только если прочитан прошлы код
				m_pChip->m_reg177662in = nScanCode & 0177;
				m_pChip->m_reg177660 |= 0200;
				m_pChip->KeyboardInterrupt(nInt);

				switch (nScanCode)
				{
					case BKKEY_LAT:
						m_BKVKBDView.SetXLatStatus(FALSE);
						OutKeyboardSstatus(STATUS_PANE_KBD_XLAT);
						break;

					case BKKEY_RUS:
						m_BKVKBDView.SetXLatStatus(TRUE);
						OutKeyboardSstatus(STATUS_PANE_KBD_XLAT);
						break;
				}
			}

			// Установим в регистре 177716 флаг нажатия клавиши
			m_pChip->m_reg177716in &= ~0100;
		}
		break;

		case BKKeyType::BKKEY_LSHIFT:
		case BKKeyType::BKKEY_RSHIFT:
			m_BKVKBDView.SetShiftStatus(TRUE);
			break;

		case BKKeyType::BKKEY_CTRL:
			m_BKVKBDView.SetSUStatus(TRUE);
			OutKeyboardSstatus(STATUS_PANE_KBD_SU);
			break;

		case BKKeyType::BKKEY_ALT:
			m_BKVKBDView.SetAR2Status(TRUE);
			OutKeyboardSstatus(STATUS_PANE_KBD_AR2);
			break;

		case BKKeyType::BKKEY_ZAGL:
			if (!(GetKeyState(VK_CAPITAL) & 1))
			{
				m_BKVKBDView.SetCapitalStatus(FALSE);
				keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);               // Нажать и отпустить клавишу
				keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); //
			}

			break;

		case BKKeyType::BKKEY_STR:
			if (GetKeyState(VK_CAPITAL) & 1)
			{
				m_BKVKBDView.SetCapitalStatus(TRUE);
				keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | 0, 0);               // Нажать и отпустить клавишу
				keybd_event(VK_CAPITAL, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); //
			}

			break;

		case BKKeyType::BKKEY_STOP:
			m_pChip->StopInterrupt(); //нажали на кнопку стоп
			break;
	}

	return S_OK;
}

LRESULT CMainFrame::OnVirtualKeyUp(WPARAM wParam, LPARAM lParam)
{
	switch (wParam)
	{
		case BKKeyType::BKKEY_REGULAR:
			// Установим в регистре 177716 флаг отпускания клавиши
			m_pChip->m_reg177716in |= 0100;
			break;

		case BKKeyType::BKKEY_LSHIFT:
		case BKKeyType::BKKEY_RSHIFT:
			m_BKVKBDView.SetShiftStatus(FALSE);
			break;

		case BKKeyType::BKKEY_CTRL:
			m_BKVKBDView.SetSUStatus(FALSE);
			OutKeyboardSstatus(STATUS_PANE_KBD_SU);
			break;

		case BKKeyType::BKKEY_ALT:
			m_BKVKBDView.SetAR2Status(FALSE);
			OutKeyboardSstatus(STATUS_PANE_KBD_AR2);
			break;

		case BKKeyType::BKKEY_STOP:
			m_pChip->UnStopInterrupt(); //отжали кнопку стоп
			break;
	}

	return S_OK;
}

void CMainFrame::EmulateKeyDown(UINT nChar, UINT nFlags)
{
	// Если нажали СТОП
	if (nChar == VK_PAUSE || nChar == VK_DIVIDE)
	{
		m_pChip->StopInterrupt(); //нажали на кнопку стоп
	}
	// Если нажали Шифт
	else if (nChar == VK_SHIFT)
	{
		m_BKVKBDView.SetShiftStatus(TRUE);
	}
	// Если нажали СУ (Любой Ctrl)
	else if (nChar == VK_CONTROL)
	{
		m_BKVKBDView.SetSUStatus(TRUE);
		OutKeyboardSstatus(STATUS_PANE_KBD_SU);
	}
	/* кнопки Загл и Стр будем эмулировать капслоком. капслок включен - стр включено, выключен - загл включено, или наоборот */
	else if (nChar == VK_CAPITAL)
	{
		m_BKVKBDView.SetCapitalStatus(!m_BKVKBDView.isCapitalPress());
		OutKeyboardSstatus(STATUS_PANE_KBD_CAPS);
	}
	else
	{
		if (!m_bIsKeyPress)
		{
			if (g_Config.m_bBKKeyboard) //отключаем автоповтор, как на реальной клавиатуре БК
			{
				m_bIsKeyPress = TRUE;
			}

			// Запишем код клавиши в регистр 177662
			WORD nScanCode = 0;
			WORD nInt = 0;
			BOOL bSuccess = m_BKVKBDView.TranslateKey(nChar, (BOOL)(nFlags & KF_EXTENDED), &nScanCode, &nInt);

			if (bSuccess)
			{
				switch (nScanCode)
				{
					case BKKEY_LAT:
						m_BKVKBDView.SetXLatStatus(FALSE);
						OutKeyboardSstatus(STATUS_PANE_KBD_XLAT);
						break;

					case BKKEY_RUS:
						m_BKVKBDView.SetXLatStatus(TRUE);
						OutKeyboardSstatus(STATUS_PANE_KBD_XLAT);
						break;
				}

				//если ещё прошлый код не прочитали, новый игнорируем.
				if ((m_pChip->m_reg177660 & 0200) == 0)
				{
					//сюда заходим только если прочитан прошлый код
					m_pChip->m_reg177662in = nScanCode & 0177;
					m_pChip->m_reg177660 |= 0200;
					m_pChip->KeyboardInterrupt((m_BKVKBDView.isAR2Press()) ? INTERRUPT_274 : nInt);
				}
			}
		}

		// Установим в регистре 177716 флаг нажатия клавиши
		m_pChip->m_reg177716in &= ~0100;
	}
}

void CMainFrame::EmulateKeyUp(UINT nChar, UINT nFlags)
{
	if (nChar == VK_PAUSE || nChar == VK_DIVIDE)
	{
		m_pChip->UnStopInterrupt(); //отжали кнопку стоп
	}
	else if (nChar == VK_SHIFT)
	{
		m_BKVKBDView.SetShiftStatus(FALSE);
	}
	else if (nChar == VK_CONTROL)
	{
		m_BKVKBDView.SetSUStatus(FALSE);
		OutKeyboardSstatus(STATUS_PANE_KBD_SU);
	}
	else
	{
		m_bIsKeyPress = FALSE;
		// Установим в регистре 177716 флаг отпускания клавиши
		m_pChip->m_reg177716in |= 0100;
	}
}

void CMainFrame::OutKeyboardSstatus(int pane)
{
	CString str;

	switch (pane)
	{
		case STATUS_PANE_KBD_XLAT:
			str = (m_BKVKBDView.isXLatMode()) ? _T("РУС") : _T("ЛАТ");
			break;

		case STATUS_PANE_KBD_CAPS:
			str = (m_BKVKBDView.isCapitalPress()) ? _T("ЗАГЛ") : _T("СТР");
			break;

		case STATUS_PANE_KBD_AR2:
			str = (m_BKVKBDView.isAR2Press()) ? _T("АР2") : _T("");
			break;

		case STATUS_PANE_KBD_SU:
			str = (m_BKVKBDView.isSUPress()) ? _T("СУ") : _T("");
			break;

		default:
			str = _T("");
	}

	m_wndStatusBar.SetPaneText(pane, str);
}

void CMainFrame::SetupConfiguration(CONFIG_BKMODEL_STRING nConf)
{
	g_Config.SetBKModelNumber(nConf);
	g_Config.m_BKFDDModel = g_mstrConfigBKModelParameters[nConf].nBKFDDModel;

	if (!ConfigurationConstructor(g_mstrConfigBKModelParameters[nConf].nBKModel))
	{
		//надо вообще закрыть прогу. нечего тут делать.
		CBKMessageBox(g_pstrErrors[BK_ERROR_MFBKBOARDERROR], MB_OK);
		theApp.ExitInstance();
	}
	else
	{
		MakeWindowTitle();
	}
}


BOOL CMainFrame::ConfigurationConstructor(MSF_CONF configuration, BOOL bStart)
{
	BOOL bReopenMemMap = FALSE;

	if (m_pChip)
	{
		// Если конфигурация уже существует, удалим её
		StopAll();  //сперва всё остановим

		if (m_lockBKMemViewOpen.IsLocked())   //если была открыта карта памяти
		{
			bReopenMemMap = TRUE;   //скажем, что после её надо будет переоткрыть
			m_pBKMemView->GetWindowRect(m_rectMemMap); //а пока закроем
			m_pBKMemView->DestroyWindow();
			SAFE_DELETE(m_pBKMemView);
			m_lockBKMemViewOpen.UnLock();
		}

		SAFE_DELETE(m_pChip);   //удалим конфигурацию
	}

	m_nSecondsCounter = 0;

	// создадим новую конфигурацию
	switch (configuration)
	{
		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_FDD;
			m_pChip->SetFDDType(g_Config.m_BKFDDModel);
			break;

		case MSF_CONF_BK11:
			m_pChip = new CMotherBoard_11;
			break;

		case MSF_CONF_BK11_FDD:
			m_pChip = new CMotherBoard_11_FDD;
			m_pChip->SetFDDType(g_Config.m_BKFDDModel);
			break;

		case MSF_CONF_BK11M:
			m_pChip = new CMotherBoard_11M;
			break;

		case MSF_CONF_BK11M_FDD:
			m_pChip = new CMotherBoard_11M_FDD;
			m_pChip->SetFDDType(g_Config.m_BKFDDModel);
			break;

		default:
			ASSERT(FALSE);
			return FALSE;
	}

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

	//переинициализируем её
	AttachObjects();

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

	//наглядно отобразим, что и в каком дисководе находится
	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);
	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;
}
//выносим это в отдельную процедуру, т.к. есть вероятность неудачного прочтения MSF,
//при этом надо восстановить всё как было раньше
void CMainFrame::AttachObjects()
{
	if (!m_pChip)
	{
		return;
	}

	m_speaker.SetSampleBuffer(m_pChip->GetMTC());
	m_covox.SetSampleBuffer(m_pChip->GetMTC());
	m_ay8910.SetSampleBuffer(m_pChip->GetMTC());
	//Attach and init new instance
	m_pChip->AttachScreen(m_pScreen);
	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)
	{
		m_pChip->SetCPUFreq(g_Config.m_nCPUFrequency);
	}

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


// LRESULT CMainFrame::OnDisplayChange(WPARAM wParam, LPARAM lParam)
// {
//  // Catch this message for restoring window mode
//  if (m_pScreen->IsFullScreenMode())
//  {
//      // Not fullscreen mode means that user return to window mode
//      m_pScreen->SetWindowMode();
//  }
//
//  return S_OK;
// }

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); //после выхода из полноэкранного режима окно надо полностью перерисовать
		//а то после вызова диалогов в полноэкранном режиме, при выходе из полноэкранного режима не перерисовывается меню и тулбар
	}
}


LRESULT CMainFrame::OnEmulateTape(WPARAM wParam, LPARAM lParam)
{
	EmulateTapeOperation();

	if (m_pChip)
	{
		m_pChip->m_reg177716in |= 0100; // Удалим флаг нажатия клавиши - бит 100 в 177716
		m_pChip->m_reg177660 &= ~0200;  // ещё удалим флаг готовности нового кода
	}

	return S_OK;
}


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

	// пока ошибок нет
	BOOL bError = FALSE;
	// получим адрес блока параметров из R1 (BK emt 36)
	WORD pParam = m_pChip->GetRON(R_R1);
	// Первый байт - команда: загрузка сохранение и т.п.
	WORD command = m_pChip->GetByteIndirect(pParam);
	// Установим код ошибки во втором байте (позже его поменяем)
	m_pChip->SetByteIndirect(pParam + 1, 2);
	WORD fileAddr = m_pChip->GetWordIndirect(pParam + 2);  // второе слово - адрес загрузки/сохранения
	WORD fileSize = m_pChip->GetWordIndirect(pParam + 4);  // третье слово - длина файла (для загрузки может быть 0)
	WORD readAddr = 0;
	WORD readSize = 0;
	// Внутренняя загрузка на БК
	BYTE bkName[16] = {0};   // Максимальная длина имени файла на БК - 16 байтов

	// Подбираем 16 байтовое имя файла из блока параметров
	for (int c = 0; c < 16; c++)
	{
		bkName[c] = m_pChip->GetByteIndirect(pParam + 6 + c);
	}

	CString strFileName = BKToUNICODE(bkName, 16); //тут надо перекодировать  имя файла из кои8 в unicode
	strFileName.Trim(); //удаляем пробелы в конце файла, а в середине - оставляем

	////////////////////////////////////////////////////////////////////////////
	// Файловые операции
	////////////////////////////////////////////////////////////////////////////
	// если "Загрузка"
	if (command == 3)
	{
		////////////////////////////////////////////////////////////////////////////
		// Загрузка файла
		////////////////////////////////////////////////////////////////////////////
		if (!g_Config.m_bEmulateLoadTape)
		{
			return;
		}

		if (!m_strBinFileName.IsEmpty())
		{
			strFileName = m_strBinFileName;
			m_strBinFileName = _T("");
		}

		if (strFileName.SpanExcluding(_T(" ")) == "")
		{
			// Если имя пустое - то покажем диалог выбора файла.
			// Запомним текущую директорию
			CString strCurDir;
			::GetCurrentDirectory(1024, strCurDir.GetBufferSetLength(1024));
			strCurDir.ReleaseBuffer();
			::SetCurrentDirectory(g_Config.m_strBinPath);
			CString strFilterBin;
			strFilterBin.LoadString(IDS_FILEFILTER_BIN);
			CFileDialog dlg(TRUE, NULL, NULL,
			                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
			                strFilterBin, m_pScreen->GetBackgroundWindow());
			// Зададим начальной директорией директорию с Bin файлами
			dlg.GetOFN().lpstrInitialDir = g_Config.m_strBinPath;

			if (dlg.DoModal() == IDCANCEL)
			{
				// Если нажали Отмену, установим ошибку во втором байте блока параметров
				m_pChip->SetByteIndirect(pParam + 1, 4);
				bError = TRUE; // случилась ошибка
			}

			g_Config.m_strBinPath = g_Config.GetFilePath(dlg.GetPathName());
			strFileName = dlg.GetPathName(); //вот выбранный файл
			//имя файла надо бы как-то поместить в 0352..0372 иначе некоторые глюки наблюдаются
			UNICODEtoBK(strFileName, bkName, 16, TRUE); //вот из этого массива будем потом помещать
			// восстановим текущую директорию
			::SetCurrentDirectory(strCurDir);
		}
		else
		{
			// Если имя не пустое
			if (g_Config.GetFileExt(strFileName).CompareNoCase(_T(".bin")) != 0)
			{
				strFileName += _T(".bin"); //добавляем стандартное расширение для бин файлов.
			}

			// If internal file loading
			if (g_Config.m_bSavesDefault)
			{
				// If Saves Default flag is set loading from User directory
				strFileName = g_Config.m_strSavesPath + strFileName;
			}
			else
			{
				// If not Load from Binary files directory
				strFileName = g_Config.m_strBinPath + strFileName;
			}
		}

		// Загрузим файл, если ошибок не было
		CFile file;

		if ((file.Open(strFileName, CFile::modeRead)) && (!bError))
		{
			file.Read(&readAddr, sizeof(readAddr));   // Первое слово в файле - адрес загрузки

			if (fileAddr) // Если адрес загрузки в блоке параметров не 0
			{
				readAddr = fileAddr;    // то загружать надо по указанному адресу
			}

			fileAddr = readAddr;
			file.Read(&readSize, sizeof(readSize));   // Второе слово в файле - длина
			// сплошь и рядом встречаются .bin файлы. у которых во втором слове указана длина
			// меньше, чем длина файла - 4. Это другой формат бин, у которого в начале указывается
			// адрес, длина, имя файла[16], массив[длина], КС - контрольная сумма в конце
			WORD filesz = (WORD)file.GetLength();

			if (readSize != filesz - 4)
			{
				if (readSize == filesz - 22)
				{
					file.Seek(20, SEEK_SET); //надо пропустить ненужные данные.
				}
				else
				{
					//то ошибка, непонятный формат бин, или нет конца файла
					bError = TRUE;
				}
			}

			// Загрузка по адресу readAddr
			for (int i = 0; i < readSize; i++)
			{
				BYTE val;
				file.Read(&val, sizeof(val));
				m_pChip->SetByteIndirect(readAddr++, val);
			}

			file.Close();

			// Исправляем код ошибки в блоке параметров
			if (!bError)
			{
				m_pChip->SetByteIndirect(pParam + 1, 0);
			}

			// Заполняем системные ячейки, как это делает emt 36
			m_pChip->SetWordIndirect(0264, fileAddr);
			m_pChip->SetWordIndirect(0266, readSize);
			m_pChip->SetWordIndirect(pParam + 026, fileAddr);
			m_pChip->SetWordIndirect(pParam + 030, readSize);

			//копируем прочитанное имя файла
			for (int i = 0; i < 16; i++)
			{
				m_pChip->SetByteIndirect(pParam + 032 + i, bkName[i]);
			}
		}
		else
		{
			// При ошибке покажем сообщение
			CString strError;
			strError.Format(IDS_CANT_OPEN_FILE_S, strFileName);
			CBKMessageBox message;

			if (message.Show(strError, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2) == IDYES)
			{
				m_pChip->StopCPU(FALSE);
			}
		}

		// Помещаем в R5 последний адрес, куда производилось чтение, как в emt 36
		m_pChip->SetRON(R_R5, readAddr);
		// делаем RTS PC
		m_pChip->SetRON(R_PC, m_pChip->GetWordIndirect(m_pChip->GetRON(R_SP)));
		m_pChip->SetRON(R_SP, m_pChip->GetRON(R_SP) + 2);
	}
	else if (command == 2)
	{
		////////////////////////////////////////////////////////////////////////////
		// Saving file
		////////////////////////////////////////////////////////////////////////////
		if (!g_Config.m_bEmulateSaveTape)
		{
			return;
		}

		// Check file name for empty
		if (strFileName.SpanExcluding(_T(" ")) == "")
		{
			// If empty display save dialog
			CFileDialog dlg(FALSE, NULL, NULL,
			                OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER,
			                NULL, m_pScreen->GetBackgroundWindow());
			dlg.GetOFN().lpstrInitialDir = g_Config.m_strBinPath;

			if (dlg.DoModal() != IDOK)
			{
				// Set error byte if CANCEL pressed
				m_pChip->SetByteIndirect(pParam + 1, 4);
				bError = TRUE;
			}

			g_Config.m_strBinPath = g_Config.GetFilePath(dlg.GetPathName());
			// Get File name
			strFileName = dlg.GetPathName();
		}
		else
		{
			// If name is not empty
			strFileName += _T(".bin"); //добавляем стандартное расширение для бин файлов.

			// If internal file loading
			if (g_Config.m_bSavesDefault)
			{
				// If Saves Default flag is set loading from User directory
				strFileName = g_Config.m_strSavesPath + strFileName;
			}
			else
			{
				// If not Load from Binary files directory
				strFileName = g_Config.m_strBinPath + strFileName;
			}
		}

		CFile file;

		// Save file array if no errors
		if ((file.Open(strFileName, CFile::modeCreate | CFile::modeWrite)) && (!bError))
		{
			// Write file address and length
			file.Write(&fileAddr, sizeof(fileAddr));
			file.Write(&fileSize, sizeof(fileSize));
			WORD writeAddr = fileAddr;

			for (int i = 0; i < fileSize; i++)
			{
				BYTE val = m_pChip->GetByteIndirect(writeAddr++);
				file.Write(&val, sizeof(val));
			}

			file.Close();
			// Set no error in parameter block byte
			m_pChip->SetByteIndirect(pParam + 1, 0);
			// Set last reading address like in normal emt 36
			m_pChip->SetWordIndirect(0264, fileAddr);
			m_pChip->SetWordIndirect(0266, fileSize);
			m_pChip->SetWordIndirect(pParam + 026, fileAddr);
			m_pChip->SetWordIndirect(pParam + 030, fileSize);
			WORD wp = 0; //копируем записанное имя файла

			for (int i = 0; i < 8; i++)
			{
				WORD w = m_pChip->GetWordIndirect(pParam + 06 + wp);
				m_pChip->SetWordIndirect(pParam + 032 + wp, w);
				wp += 2;
			}
		}

		// Make RTS PC
		m_pChip->SetRON(R_PC, m_pChip->GetWordIndirect(m_pChip->GetRON(R_SP)));
		m_pChip->SetRON(R_SP, m_pChip->GetRON(R_SP) + 2);
	}

	// Refresh keyboard
	m_bIsKeyPress = FALSE;
	m_BKVKBDView.SetAR2Status(FALSE);
	m_BKVKBDView.SetShiftStatus(FALSE);
	m_BKVKBDView.SetSUStatus(FALSE);
}


BOOL CMainFrame::LoadMemoryState(CString strPath)
{
	CMSFManager msf;
	BOOL bRet = FALSE;
	BOOL bReopenMemMap = FALSE; //флаг переоктрытия карты памяти

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

	if (msf.GetType() == MSF_STATE_ID && msf.GetVersion() >= MSF_VERSION_MINIMAL)
	{
		StopAll();

		//это надо делать тут, потому что в ConfigurationConstructor попадает m_pChip == NULL
		if (m_lockBKMemViewOpen.IsLocked())   //если была открыта карта памяти
		{
			bReopenMemMap = TRUE;   //скажем, что после её надо будет переоткрыть
			m_pBKMemView->GetWindowRect(m_rectMemMap); //а пока закроем
			m_pBKMemView->DestroyWindow();
			SAFE_DELETE(m_pBKMemView);
			m_lockBKMemViewOpen.UnLock();
		}

		// Сохраняем старую конфигурацию
		CMotherBoard *pOldChip = m_pChip;
		m_pChip = NULL;

		if (ConfigurationConstructor(msf.GetConfiguration(), FALSE))
		{
			if (m_pChip->RestoreState(msf, NULL))
			{
				SAFE_DELETE(pOldChip);
				bRet = TRUE;
				InitEmulator();
			}
			else
			{
				//не удалось восстановить состояние, надо вернуть старую кофигурацию.
				CBKMessageBox(IDS_ERRMSF_WRONG, MB_OK);
				SAFE_DELETE(m_pChip);
				m_pChip = pOldChip;
				//Всё надо заново переаттачивать.
				AttachObjects();
			}
		}
		else
		{
			// Неподдерживаемая конфигурация, или ошибка при создании
			CBKMessageBox(IDS_ERRMSF_WRONG, MB_OK);
			m_pChip = pOldChip;
			AttachObjects();
		}

		if (bReopenMemMap && !m_lockBKMemViewOpen.IsLocked())
		{
			OnDebugMemmap();
		}

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

	return bRet;
}


BOOL CMainFrame::SaveMemoryState(CString strPath)
{
	if (m_pChip)
	{
		CMSFManager msf;
		msf.SetConfiguration(m_pChip->GetConfiguration());

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

void CMainFrame::OnFileLoadstate()
{
	CString strFilterMSF;
	strFilterMSF.LoadString(IDS_FILEFILTER_MSF);
	CLoadMemoryDlg dlg(TRUE, NULL, NULL,
	                   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;
	strFilterMSF.LoadString(IDS_FILEFILTER_MSF);
	CFileDialog dlg(FALSE, _T("msf"), NULL,
	                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;
	strFilterTape.LoadString(IDS_FILEFILTER_TAPE);
	CString strTapeExt;
	strTapeExt.LoadString(IDS_FILEEXT_TAPE);
	CLoadTapeDlg dlg(TRUE, strTapeExt, NULL,
	                 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::OnCpuLongReset()
{
	m_bLongResetPress = TRUE;
	OnCpuResetCpu();
}

void CMainFrame::OnCpuSuResetCpu()
{
	m_BKVKBDView.SetSUStatus(TRUE);
	OnCpuResetCpu();
}

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

	m_pChip->StopCPU();
	m_nSecondsCounter = 0;
	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_BKVKBDView.isSUPress())
		{
			m_pChip->ResetCold(040000);
		}
		else
		{
			m_pChip->ResetCold(0);
		}

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

	m_bLongResetPress = FALSE;
	// Запускаем CPU
	m_pChip->RunCPU();

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


void CMainFrame::OnUpdateCpuRunbk001001(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? m_pChip->GetConfiguration() == MSF_CONF_BK1001 : FALSE);
}


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


void CMainFrame::OnUpdateCpuRunbk001001Focal(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? m_pChip->GetConfiguration() == MSF_CONF_BK1001_MSTD : FALSE);
}


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


void CMainFrame::OnUpdateCpuRunbk00100132k(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? m_pChip->GetConfiguration() == MSF_CONF_BK1001_EXT32 : FALSE);
}


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

void CMainFrame::OnUpdateCpuRunbk0011(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? m_pChip->GetConfiguration() == MSF_CONF_BK11 : FALSE);
}

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

void CMainFrame::OnUpdateCpuRunbk0011m(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? m_pChip->GetConfiguration() == MSF_CONF_BK11M : FALSE);
}

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

void CMainFrame::OnUpdateCpuRunbk001001Fdd(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK1001_FDD && g_Config.m_BKFDDModel == DEV_FDD_STD) : FALSE);
}

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

void CMainFrame::OnUpdateCpuRunbk0011Fdd(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK11_FDD && g_Config.m_BKFDDModel == DEV_FDD_STD) : FALSE);
}

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

void CMainFrame::OnUpdateCpuRunbk0011mFDD(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK11M_FDD && g_Config.m_BKFDDModel == DEV_FDD_STD) : FALSE);
}

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


void CMainFrame::OnUpdateCpuRunbk001001Fdd16k(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK1001_FDD && g_Config.m_BKFDDModel == DEV_FDD_A16M) : FALSE);
}

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

void CMainFrame::OnUpdateCpuRunbk0011mFddA16m(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK11M_FDD && g_Config.m_BKFDDModel == DEV_FDD_A16M) : FALSE);
}

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


void CMainFrame::OnUpdateCpuRunbk001001FddSmk512(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK1001_FDD && g_Config.m_BKFDDModel == DEV_FDD_SMK512) : FALSE);
}

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

void CMainFrame::OnUpdateCpuRunbk0011FddSmk512(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK11_FDD && g_Config.m_BKFDDModel == DEV_FDD_SMK512) : FALSE);
}

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

void CMainFrame::OnUpdateCpuRunbk0011mFddSmk512(CCmdUI *pCmdUI)
{
	pCmdUI->SetRadio((m_pChip) ? (m_pChip->GetConfiguration() == MSF_CONF_BK11M_FDD && g_Config.m_BKFDDModel == DEV_FDD_SMK512) : FALSE);
}

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

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:
			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::OnVkbdtypeKeys(UINT id)
{
	switch (id)
	{
		default:
		case ID_VKBDTYPE_KEYS:
			g_Config.m_nVKBDType = 0;
			m_BKVKBDView.SetKeyboardView(IDB_BITMAP_SOFT);
			break;

		case ID_VKBDTYPE_MEMBRANE:
			g_Config.m_nVKBDType = 1;
			m_BKVKBDView.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::OnUpdateCpuAccelerate(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->CanAccelerate() : FALSE);
}


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

void CMainFrame::OnCpuAccelerateSmall()
{
	if (m_pChip)
	{
		m_pChip->AccelerateCPUSmall();
	}
}

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


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

void CMainFrame::OnCpuSlowdownSmall()
{
	if (m_pChip)
	{
		m_pChip->SlowdownCPUSmall();
	}
}

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


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


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


void CMainFrame::OnUpdateOptionsEnableCovox(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(m_covox.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::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::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::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::OnUpdateOptionsEmulateBkkeyboard(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bBKKeyboard);
}


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


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


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

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

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


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


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


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


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::OnUpdateOptionsEmulateTapeSaving(CCmdUI *pCmdUI)
{
	pCmdUI->SetCheck(g_Config.m_bEmulateSaveTape);
	UpdateTapeDlgControls();
}


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

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

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

	SetFocusToBK();
}


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


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::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::OnUpdateDebugBreak(CCmdUI *pCmdUI)
{
	if (!m_pChip)
	{
		return;
	}

	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_BREAK);
		iImage = GetCmdMgr()->GetCmdImage(ID_DEBUG_BREAK);
	}
	else
	{
		strMenu.LoadString(IDS_MENU_DEBUG_CONTINUE);
		iImage = GetCmdMgr()->GetCmdImage(ID_DEBUG_START);
	}

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


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

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


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


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


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


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


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


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


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


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


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

	m_DisassembleView.Invalidate(FALSE);
}


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


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



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


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


LRESULT CMainFrame::OnCpuBreak(WPARAM wParam, LPARAM lParam)
{
	if (m_pChip)
	{
		WORD pc = m_pChip->GetRON(R_PC);
		SetDebugCtrlsState(pc);
	}

	return S_OK;
}

LRESULT CMainFrame::OnMemDumpUpdate(WPARAM wParam, LPARAM lParam)
{
	m_memoryDumpView.SetMemDump();
	return S_OK;
}

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

	m_registryDumpView.SetRegDump();
	//тормозит, если много строк на экране.
	m_memoryDumpView.SetMemDump();
}


void CMainFrame::SetDebugCtrlsState(WORD debugAddress)
{
	if (m_DisassembleView.IsVisible())
	{
		//прорисовываем окно дизассемблера
		m_pDebugger->SetCurrentAddress(debugAddress);
		//обновим адрес в поле адреса.
		m_DisassembleView.UpdateDebugAddress();
	}

	SetDebugCtrlsState();
}


void CMainFrame::SetFocusToBK()
{
	AfxGetMainWnd()->SetFocus();
}


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




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

void CMainFrame::StopAll()
{
	m_lockStopLock.Lock();
	StopTimer();

	if (m_pChip)
	{
		m_pChip->StopCPU();
		m_pChip->StopTimerThread();
	}

	m_lockStopLock.UnLock();
}

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

	StartTimer();
}

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

/* оставим код на память.
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;

    //Количество битов под пиксель
    WORD wClrBits = (WORD)(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 BYTE[pbmi->bmiHeader.biSizeImage];
    if (lpData)
    {
        //Копирование таблицы цветов в выделенную область памяти
        HDC hDC = GetDC()->GetSafeHdc();
        HDC hTempDC = CreateCompatibleDC(hDC);

        SelectObject(hTempDC, hBitmap);
        if (GetDIBits(hTempDC, hBitmap, 0, (WORD)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
	// просто такой скриншот выглядит не очень красиво, слишком мелкий и сплющенный по высоте.
	HBITMAP 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;
}

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

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

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

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

	if (pBKMemVw)
	{
		WNDCLASS wc;
		memset(&wc, 0, sizeof(wc));
		wc.style = CS_HREDRAW | CS_VREDRAW;
		wc.hInstance = AfxGetInstanceHandle();
		wc.lpfnWndProc = ::DefWindowProc;
		wc.lpszClassName = _T("MyBKMemClass");
		wc.hbrBackground = (HBRUSH)::GetStockObject(GRAY_BRUSH);
		AfxRegisterClass(&wc);

		if (pBKMemVw->Create(
		            _T("MyBKMemClass"),     //Имя класса виндовс
		            _T("Memory Map"),      //имя окна
		            WS_POPUP | WS_CAPTION | WS_SYSMENU, // window styles
		            CRect(0, 0, 200, 200),  //размер окна
		            theApp.GetMainWnd(),    //родитель окна
		            0,
		            WS_EX_TOOLWINDOW /*| WS_EX_OVERLAPPEDWINDOW*/ //extended window styles
		        ))
		{
			if (m_rectMemMap != CRect(0, 0, 0, 0))
			{
				pBKMemVw->MoveWindow(m_rectMemMap, FALSE);
			}

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

LRESULT CMainFrame::OnMemMapClose(WPARAM wParam, LPARAM lParam)
{
	m_lockBKMemViewOpen.UnLock();
	m_pBKMemView = NULL;
	return S_OK;
}


/*
//!!!
 Нужно реализовать:
 1. динамическое выпадающее меню. добавлять пункт - "Открыть в БКДЕ" с передачей имени образа в качестве параметра
 2. Придумать если возможно - редактирование образа в БКДЕ, и в это время не должно быть конфликтов с эмулятором.
    Либо нужно отмонтировать образ на время работы, а при закрытии БКДЕ как-то давать эмулятору знать, что образ
    можно назад монтировать.
    Либо останавливать эмулятор и не давать к нему доступа пока не будет закрыт БКДЕ.
    Оба способа непонятно как сделать.
*/
void CMainFrame::OnFileLoadDriveA()
{
	LoadFileImage(ID_FILE_LOADDRIVE_A, CFDDController::BK_FDD_DRIVE_A);
}
void CMainFrame::OnFileLoadDriveB()
{
	LoadFileImage(ID_FILE_LOADDRIVE_B, CFDDController::BK_FDD_DRIVE_B);
}
void CMainFrame::OnFileLoadDriveC()
{
	LoadFileImage(ID_FILE_LOADDRIVE_C, CFDDController::BK_FDD_DRIVE_C);
}
void CMainFrame::OnFileLoadDriveD()
{
	LoadFileImage(ID_FILE_LOADDRIVE_D, CFDDController::BK_FDD_DRIVE_D);
}

void CMainFrame::OnUpdateFileLoadDriveA(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->GetFDD()->GetDriveState(CFDDController::BK_FDD_DRIVE_A) : FALSE);
}
void CMainFrame::OnUpdateFileLoadDriveB(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->GetFDD()->GetDriveState(CFDDController::BK_FDD_DRIVE_B) : FALSE);
}
void CMainFrame::OnUpdateFileLoadDriveC(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->GetFDD()->GetDriveState(CFDDController::BK_FDD_DRIVE_C) : FALSE);
}
void CMainFrame::OnUpdateFileLoadDriveD(CCmdUI *pCmdUI)
{
	pCmdUI->Enable((m_pChip) ? m_pChip->GetFDD()->GetDriveState(CFDDController::BK_FDD_DRIVE_D) : FALSE);
}

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;
	strFilterIMG.LoadString(IDS_FILEFILTER_BKIMG);
	CLoadImgDlg dlg(TRUE, NULL, NULL,
	                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();
}

/*
Из-за того, что непонятно как узнать, из-под какой кнопки меню было запущено
приходится делать 4 однотипных меню и 4 однотипных функции
*/
void CMainFrame::OnFileUmountA()
{
	if (m_pChip)
	{
		m_pChip->GetFDD()->DetachImage(CFDDController::BK_FDD_DRIVE_A);
		ChangeImageIcon(ID_FILE_LOADDRIVE_A, CFDDController::BK_FDD_DRIVE_A);
	}

	g_Config.SetDriveImgName(CFDDController::BK_FDD_DRIVE_A, g_strEmptyDrive);
}

void CMainFrame::OnFileUmountB()
{
	if (m_pChip)
	{
		m_pChip->GetFDD()->DetachImage(CFDDController::BK_FDD_DRIVE_B);
		ChangeImageIcon(ID_FILE_LOADDRIVE_B, CFDDController::BK_FDD_DRIVE_B);
	}

	g_Config.SetDriveImgName(CFDDController::BK_FDD_DRIVE_B, g_strEmptyDrive);
}

void CMainFrame::OnFileUmountC()
{
	if (m_pChip)
	{
		m_pChip->GetFDD()->DetachImage(CFDDController::BK_FDD_DRIVE_C);
		ChangeImageIcon(ID_FILE_LOADDRIVE_C, CFDDController::BK_FDD_DRIVE_C);
	}

	g_Config.SetDriveImgName(CFDDController::BK_FDD_DRIVE_C, g_strEmptyDrive);
}

void CMainFrame::OnFileUmountD()
{
	if (m_pChip)
	{
		m_pChip->GetFDD()->DetachImage(CFDDController::BK_FDD_DRIVE_D);
		ChangeImageIcon(ID_FILE_LOADDRIVE_D, CFDDController::BK_FDD_DRIVE_D);
	}

	g_Config.SetDriveImgName(CFDDController::BK_FDD_DRIVE_D, g_strEmptyDrive);
}

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

	CMFCToolBarMenuButton *pParentButton = pMenuPopup->GetParentButton();

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

	int nDrive = -1;

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

		case ID_FILE_LOADDRIVE_B: nDrive = 1; break;

		case ID_FILE_LOADDRIVE_C: nDrive = 2; break;

		case ID_FILE_LOADDRIVE_D: nDrive = 3; break;
	}

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

	return CFrameWndEx::OnShowPopupMenu(pMenuPopup);
}


void CMainFrame::OnClose()
{
	g_Config.m_nCPUFrequency = m_pChip->GetCPUSpeed();
	StopAll();

	if (g_hHook)
	{
		BOOL bRes = UnhookWindowsHookEx(g_hHook);
		ASSERT(bRes);
	}

	CFrameWndEx::OnClose();
}



