xenoborg/alpha/archive/source/XenoKrnl/EmuMain.cpp
2012-01-05 00:03:04 +00:00

395 lines
12 KiB
C++

/*
* Xenoborg - Xbox Emulator
* Copyright (C) 2006-2010 blueshogun96
*
* Name:
* Desc:
*
* Changelog:
* 5/2/11 - Stopped the emulator from crashing after emulation was started
* (see EmuMMU.cpp). Also added code to halt the currently running threads
* but not the main thread.
*
* 4/25/11 - Added menu functionality code and changed CreateThread to
* _beginthreadex. Emu doesn't exit properly without a hard exit.
*
* 9/21/2010 - Doing a major overhaul on the overall code architecture
* of the emulator in general. For now, we won't use DirectDraw
* for VBlank detection until I can write a better implementation
* if needed.
*
* 5/1/2010 - Initial writing.
*/
#include "XenoKrnl.h"
#include "DbgConsole.h"
#include "EmuMain.h"
#include "Error.h"
#include "resource.h"
//--------------------------------------------------------------------------------------
// Global
//--------------------------------------------------------------------------------------
HANDLE g_hUpdateThread = NULL; // Handle to the update thread.
HANDLE g_hExecutionThread = NULL; // Handle to the code execution thread
HANDLE g_hEmuThread = NULL; // Handle to the main emulator thread.
BOOL g_bEmuIsRunning = FALSE; // Is the emulator actually running?
HWND g_EmuWnd = NULL; // The handle to the emulation window.
BOOL bVBlank = FALSE;
BOOL bVSync = FALSE;
//--------------------------------------------------------------------------------------
// Name: OpenXbeFile
// Desc: Allows the user to search for any given .xbe file to use with the emulator.
//--------------------------------------------------------------------------------------
bool OpenXbeFile( void )
{
char filter[256] = {0};
char filename[256] = {0};
OPENFILENAMEA File;
if( Xbe.m_bXbeHasBeenOpened )
XBEUnload();
strcat( filter, "XBox Executable Files (*.xbe)" );
ZeroMemory( &File, sizeof( OPENFILENAME ) );
int index = lstrlenA( filter ) + 1;
filter[index++] = '*';
filter[index++] = '.';
filter[index++] = 'x';
filter[index++] = 'b';
filter[index++] = 'e';
File.lStructSize = sizeof( OPENFILENAME );
File.hwndOwner = GetActiveWindow();
File.lpstrFilter = filter;
File.lpstrFile = filename;
File.nMaxFile = 256;
File.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;
if( !GetOpenFileNameA( &File ) )
return EmuError( "Unable to load file.\n" );
// Back up the .exe data before executing
EmuMMUSaveExeContents();
// Now, actually load the .xbe into memory.
if( !XBELoad( File.lpstrFile ) )
{
EmuError( "Unable to load Xbe file!\n" );
return 0;
}
// Hijack kernel functions
HookKrnlFunctions();
// Activate other threads
ResumeThread( g_hExecutionThread );
ResumeThread( g_hUpdateThread );
return true;
}
//--------------------------------------------------------------------------------------
// Name: ProcessMenuActivity
// Desc: Responds accordingly to clicks on the menu.
//--------------------------------------------------------------------------------------
void ProcessMenuActivity( WORD wMenuID, HWND hWnd )
{
// Interpret appropriate action per menu option
switch( wMenuID )
{
case ID_FILE_EXIT: // Exits the emulator
PostMessage( hWnd, WM_DESTROY, 0, 0 );
break;
case ID_EXECUTE_XBE: // Open an xbe file and start emulating
OpenXbeFile();
break;
}
}
//--------------------------------------------------------------------------------------
// Name: EmuWindowProcedure
// Desc: Window event handler for the UI and rendering window.
//--------------------------------------------------------------------------------------
LRESULT CALLBACK EmuWindowProcedure( HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam )
{
switch( uMessage )
{
case WM_CREATE:
return 0;
case WM_CLOSE:
case WM_DESTROY:
HaltEmuThreads();
XBEUnload();
EmuMMURestoreExeContents();
exit(0);
// PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
/*switch( wParam )
{
case VK_F1:
ResumeThread( g_hUpdateThread );
ResumeThread( g_hExecutionThread );
break;
case VK_F2:
SuspendThread( g_hUpdateThread );
SuspendThread( g_hExecutionThread );
break;
}*/
return 0;
case WM_COMMAND:
ProcessMenuActivity( (WORD) LOWORD( wParam ), hWnd );
return 0;
case WM_SYSCOMMAND:
switch( wParam )
{
case SC_MONITORPOWER:
case SC_SCREENSAVE:
return 0;
}
break;
}
return DefWindowProc( hWnd, uMessage, wParam, lParam );
}
//--------------------------------------------------------------------------------------
// Name: EmuWindowCreate
// Desc: Creates the emulation window.
//--------------------------------------------------------------------------------------
bool EmuWindowCreate( void )
{
WNDCLASSEX wc;
wc.cbSize = sizeof( WNDCLASSEX );
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wc.hIconSm = wc.hIcon;
wc.hInstance = GetModuleHandle( NULL );
wc.lpfnWndProc = EmuWindowProcedure;
wc.lpszClassName = L"Xenoborg";
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);//NULL;
RegisterClassEx( &wc );
// Create the window.
// TODO: Adjust window dimensions to compensate for title bar and menu size.
g_EmuWnd = CreateWindowEx( WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,
L"Xenoborg", L"Xenoborg", WS_OVERLAPPEDWINDOW/* & (~WS_MAXIMIZEBOX)*/,
CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
NULL, NULL, GetModuleHandle( NULL ), NULL );
if( !g_EmuWnd )
return false;
// Show the window
ShowWindow( g_EmuWnd, SW_SHOW );
UpdateWindow( g_EmuWnd );
SetForegroundWindow( g_EmuWnd );
SetFocus( g_EmuWnd );
SetActiveWindow( g_EmuWnd );
// char string[128];
// sprintf( string, "Xenoborg: %S", Xbe.m_XbeCertificate.wcTitle );
// SetWindowTextA( GetActiveWindow(), string );
return true;
}
//--------------------------------------------------------------------------------------
// Name: EmuThread
// Desc: This is the emulator's main thread. Handles initialization of the emulator's
// window and other components that are required to make it run.
//--------------------------------------------------------------------------------------
unsigned int WINAPI EmuThread( void* Params )
{
MSG msg;
// Initialize debug console
if( DbgConsoleInit() )
DbgPrintf( " Xenoborg: Emulator thread started (Thread ID: 0x%X)\n",
GetCurrentThreadId() );
// Initialize the window
if( !EmuWindowCreate() )
{
EmuError( "Error creating main window!" );
return 0;
}
// Message pump
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow( g_EmuWnd );
return 0;
}
//--------------------------------------------------------------------------------------
// Name: ExecutionThread
// Desc: Handles the execution of x86 code within the exception handling block. When an
// exception is caught, the exception handling function can either perform the
// necessary hardware emulation equivelant to the task being executed and continue
// execution, or exit the thread altogether.
//--------------------------------------------------------------------------------------
unsigned int WINAPI ExecutionThread( LPVOID Params )
{
// Signifiy (in the debug console) that the code execution thread has started and show
// the thread's ID number for debugging purposes.
DbgPrintf( ">>> Execution thread started (Thread ID: 0x%X)\n", GetCurrentThreadId() );
if( !EmuNV2AInit() )
return -1;
// Set the emulator as running
g_bEmuIsRunning = TRUE;
// Now create a function pointer that points to the entry point of
// the .xbe file.
void (*Execute)(void) = (void(*)(void)) Xbe.m_dwActualEntryPoint;
// Execute the code within the .xbe file and catch hardware read/write
// operations via MMU (see EmuMMU.cpp)
__try
{
Execute();
}
__except( ExceptionFunction( GetExceptionInformation() ) )
{
MessageBoxW( NULL, L"Exception occured!", L"Exception", MB_ICONERROR );
}
EmuError( "Exiting execution thread...\n" );
return 0;
}
//--------------------------------------------------------------------------------------
// Name: UpdateThread
// Desc: This thread is responsible for handling graphics, sound, user input, networking,
// etc. after the required Windows API components are initialized (also done in this
// thread to reduce conflicts with other threads sharing resources). This thread also
// keeps track of time sensitive operations such as VBlank emulation keeps track of
// the tick count for the HLE kernel.
//--------------------------------------------------------------------------------------
unsigned int WINAPI UpdateThread( LPVOID Params )
{
// Signifiy (in the debug console) that the emulator update thread has started and show
// the thread's ID number for debugging purposes.
DbgPrintf( ">>> Update thread started (Thread ID: 0x%X)\n", GetCurrentThreadId() );
// Wait until the emulator is ready to run.
while( !g_bEmuIsRunning );
while( TRUE )
{
// Swap VBlank status
bVBlank = !bVBlank;
// If we are in VBlank, set the VSync flag
if( bVBlank )
bVSync = TRUE;
// Is Vsync ending?
if( !bVBlank && bVSync )
{
// If so, end it.
// bVSync = FALSE;
}
// Update KeTickCount
KeTickCount = GetTickCount();
// Yield time to other threads
Sleep(1);
}
return 0;
}
//--------------------------------------------------------------------------------------
// Name: InitEmuThreads
// Desc: Initializes the code execution and VBlank timing emulation threads.
//--------------------------------------------------------------------------------------
bool InitEmuThreads()
{
if( g_bEmuIsRunning )
return false;
// Now actually create handles to the threads
g_hEmuThread = (HANDLE) _beginthreadex( NULL, 0, EmuThread, NULL, CREATE_SUSPENDED, NULL );
g_hExecutionThread = (HANDLE) _beginthreadex( NULL, 0, ExecutionThread, NULL, CREATE_SUSPENDED, NULL );
g_hUpdateThread = (HANDLE) _beginthreadex( NULL, 0, UpdateThread, NULL, CREATE_SUSPENDED, NULL );
// Activate main thread
ResumeThread( g_hEmuThread );
// ResumeThread( g_hUpdateThread );
// ResumeThread( g_hExecutionThread );
return true;
}
//--------------------------------------------------------------------------------------
// Name: CloseEmuThreads
// Desc: Closes the active threads.
//--------------------------------------------------------------------------------------
void CloseEmuThreads()
{
DbgPrintf( "Closing emu threads...\n" );
Sleep( 1000 );
g_bEmuIsRunning = FALSE;
if( g_hExecutionThread )
{
CloseHandle( g_hExecutionThread );
g_hExecutionThread = NULL;
}
if( g_hUpdateThread )
{
CloseHandle( g_hUpdateThread );
g_hUpdateThread = NULL;
}
EmuNV2AUninit();
if( g_hEmuThread )
{
CloseHandle( g_hEmuThread );
g_hEmuThread = NULL;
}
}
//--------------------------------------------------------------------------------------
// Name: HaltEmuThreads
// Desc: Halts the active threads (does not halt the main thread!)
//--------------------------------------------------------------------------------------
void HaltEmuThreads()
{
if( g_hExecutionThread )
SuspendThread( g_hExecutionThread );
if( g_hUpdateThread )
SuspendThread( g_hUpdateThread );
}