306 lines
9.2 KiB
C++
306 lines
9.2 KiB
C++
/* This file is part of BKBTL.
|
|
BKBTL is free software: you can redistribute it and/or modify it under the terms
|
|
of the GNU Lesser General Public License as published by the Free Software Foundation,
|
|
either version 3 of the License, or (at your option) any later version.
|
|
BKBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the GNU Lesser General Public License for more details.
|
|
You should have received a copy of the GNU Lesser General Public License along with
|
|
BKBTL. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
// BKBTL.cpp : Defines the entry point for the application.
|
|
|
|
#include "stdafx.h"
|
|
#include <commdlg.h>
|
|
#include <crtdbg.h>
|
|
#include <mmintrin.h>
|
|
#include <Vfw.h>
|
|
#include <CommCtrl.h>
|
|
#include <shellapi.h>
|
|
#include <timeapi.h>
|
|
|
|
#include "Main.h"
|
|
#include "Emulator.h"
|
|
#include "Views.h"
|
|
#include "Joystick.h"
|
|
#include "util/BitmapFile.h"
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Global Variables
|
|
|
|
HINSTANCE g_hInst = NULL; // current instance
|
|
HWND g_hwnd = NULL;
|
|
long m_nMainLastFrameTicks = 0;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Forward declarations
|
|
|
|
BOOL InitInstance(HINSTANCE, int);
|
|
void DoneInstance();
|
|
void ParseCommandLine();
|
|
|
|
LPCTSTR g_CommandLineHelp =
|
|
_T("Usage: BKBTL [options]\r\n\r\n")
|
|
_T("Command line options:\r\n\r\n")
|
|
_T("/h /help\r\n\tShow command line options (this box)\r\n")
|
|
_T("/autostart /autostarton\r\n\tStart emulation on window open\r\n")
|
|
_T("/noautostart /autostartoff\r\n\tDo not start emulation on window open\r\n")
|
|
_T("/debug /debugon /debugger\r\n\tSwitch to debug mode\r\n")
|
|
_T("/nodebug /debugoff\r\n\tSwitch off the debug mode\r\n")
|
|
_T("/sound /soundon\r\n\tTurn sound on\r\n")
|
|
_T("/nosound /soundoff\r\n\tTurn sound off\r\n")
|
|
_T("/diskN:filePath\r\n\tAttach disk image, N=0..3\r\n");
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
int APIENTRY _tWinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPTSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
|
UNREFERENCED_PARAMETER(lpCmdLine);
|
|
|
|
#ifdef _DEBUG
|
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF);
|
|
int n = 0;
|
|
_CrtSetBreakAlloc(n);
|
|
#endif
|
|
|
|
g_hInst = hInstance; // Store instance handle in our global variable
|
|
|
|
LARGE_INTEGER nFrameStartTime;
|
|
nFrameStartTime.QuadPart = 0;
|
|
|
|
// Initialize global strings
|
|
LoadString(g_hInst, IDS_APP_TITLE, g_szTitle, MAX_LOADSTRING);
|
|
LoadString(g_hInst, IDC_APPLICATION, g_szWindowClass, MAX_LOADSTRING);
|
|
MainWindow_RegisterClass();
|
|
|
|
// Perform application initialization
|
|
if (! InitInstance(hInstance, nCmdShow))
|
|
return FALSE;
|
|
|
|
HACCEL hAccelTable = ::LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_APPLICATION));
|
|
|
|
if (Option_ShowHelp)
|
|
::PostMessage(g_hwnd, WM_COMMAND, ID_HELP_COMMAND_LINE_HELP, NULL);
|
|
|
|
LARGE_INTEGER nPerformanceFrequency;
|
|
::QueryPerformanceFrequency(&nPerformanceFrequency);
|
|
|
|
TIMECAPS caps;
|
|
VERIFY(!::timeGetDevCaps(&caps, sizeof(caps)));
|
|
VERIFY(!::timeBeginPeriod(caps.wPeriodMin));
|
|
|
|
// Main message loop
|
|
MSG msg;
|
|
for (;;)
|
|
{
|
|
::QueryPerformanceCounter(&nFrameStartTime);
|
|
|
|
if (!g_okEmulatorRunning)
|
|
::Sleep(1);
|
|
else
|
|
{
|
|
if (!Emulator_SystemFrame()) // Breakpoint hit
|
|
{
|
|
Emulator_Stop();
|
|
// Turn on degugger if not yet
|
|
if (!Settings_GetDebug())
|
|
::PostMessage(g_hwnd, WM_COMMAND, ID_VIEW_DEBUG, 0);
|
|
else
|
|
::FlashWindow(g_hwnd, TRUE);
|
|
}
|
|
|
|
ScreenView_RedrawScreen();
|
|
}
|
|
|
|
// Process all queue
|
|
while (::PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
goto endprog;
|
|
|
|
if (::TranslateAccelerator(g_hwnd, hAccelTable, &msg))
|
|
continue;
|
|
|
|
::TranslateMessage(&msg);
|
|
::DispatchMessage(&msg);
|
|
}
|
|
|
|
if (g_okEmulatorRunning && !Settings_GetSound())
|
|
{
|
|
WORD speed = Settings_GetRealSpeed();
|
|
if (speed == 0)
|
|
::Sleep(0); // Speed MAX, consume 100% of one CPU core
|
|
else
|
|
{
|
|
LONGLONG nFrameDelay;
|
|
switch (speed)
|
|
{
|
|
case 0x7ffd: nFrameDelay = 1000ll / FRAMERATE * 10; break; // Speed 10%
|
|
case 0x7ffe: nFrameDelay = 1000ll / FRAMERATE * 4; break; // Speed 25%
|
|
case 0x7fff: nFrameDelay = 1000ll / FRAMERATE * 2; break; // Speed 50%
|
|
case 2: nFrameDelay = 1000ll / FRAMERATE / 2; break; // Speed 200%
|
|
case 3: nFrameDelay = 1000ll / FRAMERATE / 4; break; // Speed 400%
|
|
default: // Speed 100%
|
|
nFrameDelay = 1000ll / FRAMERATE; // 1000 millisec / 50 = 20 millisec
|
|
break;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
LARGE_INTEGER nFrameFinishTime; // Frame start time
|
|
::QueryPerformanceCounter(&nFrameFinishTime);
|
|
LONGLONG nTimeElapsed = (nFrameFinishTime.QuadPart - nFrameStartTime.QuadPart)
|
|
* 1000ll / nPerformanceFrequency.QuadPart;
|
|
if (nTimeElapsed <= 0 || nTimeElapsed >= nFrameDelay)
|
|
break;
|
|
LONGLONG nDelayRemaining = nFrameDelay - nTimeElapsed;
|
|
::Sleep((DWORD)(nDelayRemaining / 2));
|
|
}
|
|
}
|
|
}
|
|
|
|
//// Time bomb for perfomance analysis
|
|
//if (Emulator_GetUptime() >= 300) // 5 minutes
|
|
// ::PostQuitMessage(0);
|
|
}
|
|
endprog:
|
|
|
|
::timeEndPeriod(caps.wPeriodMin);
|
|
DoneInstance();
|
|
|
|
#ifdef _DEBUG
|
|
if (_CrtDumpMemoryLeaks())
|
|
::MessageBeep(MB_ICONEXCLAMATION);
|
|
#endif
|
|
|
|
return (int) msg.wParam;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: InitInstance(HINSTANCE, int)
|
|
//
|
|
// PURPOSE: Saves instance handle and creates main window
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// In this function, we save the instance handle in a global variable and
|
|
// create and display the main program window.
|
|
//
|
|
BOOL InitInstance(HINSTANCE /*hInstance*/, int /*nCmdShow*/)
|
|
{
|
|
INITCOMMONCONTROLSEX ics; ics.dwSize = sizeof(ics);
|
|
ics.dwICC = ICC_WIN95_CLASSES;
|
|
InitCommonControlsEx(&ics);
|
|
|
|
BitmapFile_Init();
|
|
|
|
#if !defined(PRODUCT)
|
|
DebugLogClear();
|
|
#endif
|
|
Settings_Init();
|
|
Joystick_Init();
|
|
Joystick_SelectJoystick(Settings_GetJoystick());
|
|
|
|
ParseCommandLine(); // Override settings by command-line option if needed
|
|
|
|
if (!Emulator_Init())
|
|
return FALSE;
|
|
|
|
int conf = Settings_GetConfiguration();
|
|
if (conf == 0) conf = BK_CONF_BK0010_BASIC;
|
|
if (!Emulator_InitConfiguration((BKConfiguration)conf))
|
|
return FALSE;
|
|
|
|
Emulator_SetSound(Settings_GetSound() != 0);
|
|
Emulator_SetSpeed(Settings_GetRealSpeed());
|
|
Emulator_SetCovox(Settings_GetSoundCovox() != 0);
|
|
Emulator_SetSoundAY(Settings_GetSoundAY() != 0);
|
|
|
|
if (!CreateMainWindow())
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Instance finalization
|
|
void DoneInstance()
|
|
{
|
|
ScreenView_Done();
|
|
DisasmView_Done();
|
|
|
|
Emulator_Done();
|
|
|
|
Joystick_Done();
|
|
|
|
BitmapFile_Done();
|
|
|
|
Settings_Done();
|
|
}
|
|
|
|
void ParseCommandLine()
|
|
{
|
|
LPTSTR commandline = ::GetCommandLine();
|
|
|
|
int argnum = 0;
|
|
LPTSTR* args = CommandLineToArgvW(commandline, &argnum);
|
|
|
|
for (int curargn = 1; curargn < argnum; curargn++)
|
|
{
|
|
LPTSTR arg = args[curargn];
|
|
|
|
if (_tcscmp(arg, _T("/help")) == 0 || _tcscmp(arg, _T("/h")) == 0)
|
|
{
|
|
Option_ShowHelp = true;
|
|
}
|
|
else if (_tcscmp(arg, _T("/autostart")) == 0 || _tcscmp(arg, _T("/autostarton")) == 0)
|
|
{
|
|
Settings_SetAutostart(TRUE);
|
|
}
|
|
else if (_tcscmp(arg, _T("/autostartoff")) == 0 || _tcscmp(arg, _T("/noautostart")) == 0)
|
|
{
|
|
Settings_SetAutostart(FALSE);
|
|
}
|
|
else if (_tcscmp(arg, _T("/debug")) == 0 || _tcscmp(arg, _T("/debugon")) == 0 || _tcscmp(arg, _T("/debugger")) == 0)
|
|
{
|
|
Settings_SetDebug(TRUE);
|
|
}
|
|
else if (_tcscmp(arg, _T("/debugoff")) == 0 || _tcscmp(arg, _T("/nodebug")) == 0)
|
|
{
|
|
Settings_SetDebug(FALSE);
|
|
}
|
|
else if (_tcscmp(arg, _T("/sound")) == 0 || _tcscmp(arg, _T("/soundon")) == 0)
|
|
{
|
|
Settings_SetSound(TRUE);
|
|
}
|
|
else if (_tcscmp(arg, _T("/soundoff")) == 0 || _tcscmp(arg, _T("/nosound")) == 0)
|
|
{
|
|
Settings_SetSound(FALSE);
|
|
}
|
|
else if (_tcslen(arg) > 7 && _tcsncmp(arg, _T("/disk"), 5) == 0) // "/diskN:filePath", N=A..D
|
|
{
|
|
if (arg[5] >= _T('A') && arg[5] <= _T('D') && arg[6] == ':')
|
|
{
|
|
int slot = arg[5] - _T('A');
|
|
LPCTSTR filePath = arg + 7;
|
|
Settings_SetFloppyFilePath(slot, filePath);
|
|
}
|
|
}
|
|
//TODO: "/state:filepath"
|
|
}
|
|
|
|
::LocalFree(args);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|