2006-07-12 13:53:14 +00:00
|
|
|
/*
|
|
|
|
* Sega Model 3 Emulator
|
|
|
|
* Copyright (C) 2003 Bart Trzynadlowski, Ville Linde, Stefano Teso
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License Version 2 as published
|
|
|
|
* by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program 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 General Public License
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program (license.txt); if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
|
|
/* Windows Main */
|
|
|
|
/******************************************************************/
|
|
|
|
|
|
|
|
#include "model3.h"
|
|
|
|
#ifdef RENDERER_D3D
|
|
|
|
#include "dx_render.h"
|
|
|
|
#else // RENDERER_GL
|
|
|
|
#include "win_gl.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define XRES (496)
|
|
|
|
#define YRES (384)
|
|
|
|
|
|
|
|
static CHAR app_title[] = "Supermodel";
|
|
|
|
static CHAR app_version[] = "1.0";
|
|
|
|
static CHAR class_name[] = "MODEL3";
|
|
|
|
|
|
|
|
static CHAR CONFIG_FILE[] = "config.xml";
|
|
|
|
|
|
|
|
HWND main_window;
|
|
|
|
|
|
|
|
// Window Procedure prototype
|
|
|
|
static LRESULT CALLBACK win_window_proc(HWND, UINT, WPARAM, LPARAM);
|
|
|
|
|
|
|
|
static BOOL win_register_class(void)
|
|
|
|
{
|
|
|
|
WNDCLASSEX wcex;
|
|
|
|
|
|
|
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
|
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
|
|
wcex.lpfnWndProc = (WNDPROC)win_window_proc;
|
|
|
|
wcex.cbClsExtra = 0;
|
|
|
|
wcex.cbWndExtra = 0;
|
|
|
|
wcex.hInstance = GetModuleHandle(NULL);
|
|
|
|
wcex.hIcon = NULL;
|
|
|
|
wcex.hIconSm = NULL;
|
|
|
|
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
|
|
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
|
|
|
wcex.lpszMenuName = NULL;
|
|
|
|
wcex.lpszClassName = class_name;
|
|
|
|
|
|
|
|
if (FAILED(RegisterClassEx(&wcex))) // MinGW: "comparison is always false due to limited range of data"
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL win_create_window(UINT xres, UINT yres)
|
|
|
|
{
|
|
|
|
DWORD frame_width, frame_height, caption_height;
|
|
|
|
int width, height, window_width, window_height;
|
|
|
|
|
|
|
|
window_width = xres;
|
|
|
|
window_height = yres;
|
|
|
|
|
|
|
|
frame_width = GetSystemMetrics(SM_CXSIZEFRAME);
|
|
|
|
frame_height = GetSystemMetrics(SM_CYSIZEFRAME);
|
|
|
|
caption_height = GetSystemMetrics(SM_CYCAPTION);
|
|
|
|
|
|
|
|
width = (window_width - 1) + (frame_width * 2);
|
|
|
|
height = (window_height - 1) + (frame_height * 2) + caption_height;
|
|
|
|
|
|
|
|
main_window = CreateWindow(class_name,
|
|
|
|
app_title,
|
|
|
|
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | // required for OpenGL
|
|
|
|
WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX,
|
|
|
|
CW_USEDEFAULT, CW_USEDEFAULT, // Window X & Y coords
|
|
|
|
width - 1, height - 1, // Width & Height
|
|
|
|
NULL, NULL, // Parent Window & Menu
|
|
|
|
GetModuleHandle(NULL), NULL );
|
|
|
|
|
|
|
|
if (!main_window)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NOTE: This should actually return an error if something goes wrong, but
|
|
|
|
* it doesn't matter now. This stuff probably needs to be rewritten anyway ;)
|
|
|
|
*/
|
|
|
|
static void win_destroy(void)
|
|
|
|
{
|
|
|
|
#ifdef RENDERER_D3D
|
|
|
|
d3d_shutdown();
|
|
|
|
#else // RENDERER_GL
|
|
|
|
win_gl_shutdown();
|
|
|
|
#endif
|
|
|
|
DestroyWindow(main_window);
|
|
|
|
UnregisterClass(class_name, GetModuleHandle(NULL));
|
|
|
|
}
|
|
|
|
|
|
|
|
void *malloc_exec(int length)
|
|
|
|
{
|
|
|
|
void *ptr;
|
|
|
|
ptr = VirtualAlloc(NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
|
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
{
|
|
|
|
error("malloc_exec %d failed\n", length);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_exec(void *ptr)
|
|
|
|
{
|
|
|
|
if (VirtualFree(ptr, 0, MEM_RELEASE) == FALSE)
|
|
|
|
{
|
|
|
|
error("free_exec failed\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL check_cpu_features(void)
|
|
|
|
{
|
|
|
|
BOOL features_ok = TRUE;
|
|
|
|
char cpuname[256];
|
|
|
|
UINT32 cpu_version;
|
|
|
|
UINT32 cpu_features;
|
|
|
|
|
|
|
|
memset(cpuname, 0, sizeof(cpuname));
|
|
|
|
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
// get cpu name string
|
|
|
|
mov eax, 0x80000002
|
|
|
|
cpuid
|
|
|
|
mov dword ptr [cpuname+0 ], eax
|
|
|
|
mov dword ptr [cpuname+4 ], ebx
|
|
|
|
mov dword ptr [cpuname+8 ], ecx
|
|
|
|
mov dword ptr [cpuname+12 ], edx
|
|
|
|
mov eax, 0x80000003
|
|
|
|
cpuid
|
|
|
|
mov dword ptr [cpuname+16 ], eax
|
|
|
|
mov dword ptr [cpuname+20 ], ebx
|
|
|
|
mov dword ptr [cpuname+24 ], ecx
|
|
|
|
mov dword ptr [cpuname+28 ], edx
|
|
|
|
mov eax, 0x80000004
|
|
|
|
cpuid
|
|
|
|
mov dword ptr [cpuname+32 ], eax
|
|
|
|
mov dword ptr [cpuname+36 ], ebx
|
|
|
|
mov dword ptr [cpuname+40 ], ecx
|
|
|
|
mov dword ptr [cpuname+44 ], edx
|
|
|
|
|
|
|
|
// get cpu version and features
|
|
|
|
mov eax, 1
|
|
|
|
cpuid
|
|
|
|
mov [cpu_version], eax
|
|
|
|
mov [cpu_features], edx
|
|
|
|
}
|
|
|
|
message(0, "CPU: %s", cpuname);
|
|
|
|
if ((cpu_features & (1 << 15)) == 0)
|
|
|
|
{
|
|
|
|
message(0, "CPU doesn't support Conditional Move/Compare instructions");
|
|
|
|
features_ok = FALSE;
|
|
|
|
}
|
|
|
|
if ((cpu_features & (1 << 23)) == 0)
|
|
|
|
{
|
|
|
|
message(0, "CPU doesn't support MMX instructions");
|
|
|
|
features_ok = FALSE;
|
|
|
|
}
|
|
|
|
if ((cpu_features & (1 << 25)) == 0)
|
|
|
|
{
|
|
|
|
message(0, "CPU doesn't support SSE instructions");
|
|
|
|
features_ok = FALSE;
|
|
|
|
}
|
|
|
|
if ((cpu_features & (1 << 26)) == 0)
|
|
|
|
{
|
|
|
|
message(0, "CPU doesn't support SSE2 instructions");
|
|
|
|
features_ok = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (features_ok == FALSE)
|
|
|
|
{
|
|
|
|
message(0, "The CPU doesn't meet the requirements, the program will not run further.");
|
|
|
|
}
|
|
|
|
return features_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
MSG msg;
|
|
|
|
BOOL quit = FALSE, do_fps;
|
|
|
|
INT64 freq, time_start, time_end;
|
|
|
|
char title[256];
|
|
|
|
double fps = 0.0;
|
|
|
|
int frame = 0;
|
|
|
|
|
|
|
|
if(argc < 2) {
|
|
|
|
// Show usage
|
|
|
|
printf("ERROR: not enough arguments.\n\n");
|
|
|
|
printf("Usage: m3.exe [romset]\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
message(0, "%s v%s\n", app_title, app_version);
|
|
|
|
|
|
|
|
if (check_cpu_features() == FALSE)
|
|
|
|
{
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2006-07-12 18:56:25 +00:00
|
|
|
#if RENDERER_D3D
|
2006-07-12 13:53:14 +00:00
|
|
|
if (d3d_pre_init() == FALSE)
|
|
|
|
{
|
|
|
|
message(0, "The video card doesn't meet the requirements, the program will not run further.");
|
|
|
|
exit(1);
|
|
|
|
}
|
2006-07-12 18:56:25 +00:00
|
|
|
#endif
|
2006-07-12 13:53:14 +00:00
|
|
|
|
|
|
|
message(0, "");
|
|
|
|
|
|
|
|
// Load config
|
|
|
|
|
|
|
|
if (parse_config(CONFIG_FILE) == FALSE)
|
|
|
|
{
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
m3_config.layer_enable = 0xF;
|
|
|
|
|
|
|
|
// Parse command-line
|
|
|
|
|
|
|
|
strncpy(m3_config.game_id, argv[1], 8);
|
|
|
|
m3_config.game_id[8] = '\0'; // in case game name was 8 or more chars
|
|
|
|
|
|
|
|
if (stricmp(m3_config.game_id, "lostwsga") == 0)
|
|
|
|
{
|
|
|
|
m3_config.has_lightgun = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some initialization
|
|
|
|
|
|
|
|
if (!win_register_class())
|
|
|
|
{
|
|
|
|
message(0, "win_register_class failed.");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (!win_create_window(XRES, YRES))
|
|
|
|
{
|
|
|
|
message(0, "win_create_window failed.");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (model3_load() == FALSE)
|
|
|
|
{
|
|
|
|
message(0, "ROM loading failed");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
model3_init();
|
|
|
|
model3_reset();
|
|
|
|
|
2006-07-12 18:56:25 +00:00
|
|
|
#if RENDERER_D3D
|
2006-07-12 13:53:14 +00:00
|
|
|
if (!d3d_init(main_window))
|
|
|
|
{
|
|
|
|
message(0, "d3d_init failed.");
|
|
|
|
exit(1);
|
|
|
|
}
|
2006-07-12 18:56:25 +00:00
|
|
|
#else
|
|
|
|
win_gl_init(XRES, YRES);
|
|
|
|
#endif
|
2006-07-12 13:53:14 +00:00
|
|
|
|
|
|
|
if (osd_input_init() == FALSE)
|
|
|
|
{
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set window title to show the game name
|
|
|
|
sprintf(title, "%s: %s", app_title, m3_config.game_name);
|
|
|
|
SetWindowText(main_window, title);
|
|
|
|
|
|
|
|
// Now that everything works, we can show the window
|
|
|
|
|
|
|
|
ShowWindow(main_window, SW_SHOWNORMAL);
|
|
|
|
SetForegroundWindow(main_window);
|
|
|
|
SetFocus(main_window);
|
|
|
|
UpdateWindow(main_window);
|
|
|
|
|
|
|
|
if (m3_config.fullscreen && !m3_config.has_lightgun)
|
|
|
|
{
|
|
|
|
ShowCursor(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(QueryPerformanceFrequency((LARGE_INTEGER *)&freq))
|
|
|
|
do_fps = TRUE;
|
|
|
|
else
|
|
|
|
do_fps = FALSE;
|
|
|
|
|
2006-07-12 18:56:25 +00:00
|
|
|
QueryPerformanceCounter((LARGE_INTEGER *)&time_start);
|
|
|
|
QueryPerformanceCounter((LARGE_INTEGER *)&time_end);
|
|
|
|
|
2006-07-12 13:53:14 +00:00
|
|
|
memset(&msg, 0, sizeof(MSG));
|
|
|
|
while (quit == FALSE)
|
|
|
|
{
|
|
|
|
//QueryPerformanceCounter((LARGE_INTEGER *)&time_start);
|
|
|
|
|
|
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
|
|
{
|
|
|
|
if (msg.message == WM_QUIT)
|
|
|
|
quit = TRUE;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
model3_run_frame();
|
|
|
|
frame++;
|
|
|
|
|
|
|
|
// gather profiler stats
|
|
|
|
|
|
|
|
//QueryPerformanceCounter((LARGE_INTEGER *)&time_end);
|
|
|
|
if (frame >= 5)
|
|
|
|
{
|
|
|
|
frame = 0;
|
|
|
|
QueryPerformanceCounter((LARGE_INTEGER *)&time_end);
|
|
|
|
fps = 5.0 / ((double)(time_end - time_start) / freq);
|
|
|
|
|
|
|
|
time_start = time_end;
|
|
|
|
}
|
|
|
|
|
2006-07-12 18:56:25 +00:00
|
|
|
#if RENDERER_D3D
|
2006-07-12 13:53:14 +00:00
|
|
|
if (m3_config.show_fps)
|
|
|
|
{
|
|
|
|
//fps = 1.0 / ((double)(time_end - time_start) / freq);
|
|
|
|
sprintf(title, "FPS: %.3f", fps);
|
|
|
|
osd_renderer_draw_text(2, 2, title, 0xffff0000, TRUE);
|
|
|
|
}
|
2006-07-12 18:56:25 +00:00
|
|
|
#else
|
|
|
|
sprintf(title, "%s: %s, FPS: %.3f", app_title, m3_config.game_name, fps);
|
|
|
|
SetWindowText(main_window, title);
|
|
|
|
#endif
|
2006-07-12 13:53:14 +00:00
|
|
|
|
|
|
|
//osd_renderer_draw_text(2, 2, title, 0x00ff0000, TRUE);
|
|
|
|
|
|
|
|
osd_renderer_blit();
|
|
|
|
|
|
|
|
#ifdef _PROFILE_
|
|
|
|
profile_print(prof);
|
|
|
|
|
|
|
|
printf(prof);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
//for (i = 0; i < 32; i += 4)
|
|
|
|
// printf("R%d=%08X\tR%d=%08X\tR%d=%08X\tR%d=%08X\n",
|
|
|
|
// i + 0, ppc_get_reg(PPC_REG_R0 + i + 0),
|
|
|
|
// i + 1, ppc_get_reg(PPC_REG_R0 + i + 1),
|
|
|
|
// i + 2, ppc_get_reg(PPC_REG_R0 + i + 2),
|
|
|
|
// i + 3, ppc_get_reg(PPC_REG_R0 + i + 3));
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void osd_warning()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void osd_error(CHAR * string)
|
|
|
|
{
|
|
|
|
printf("ERROR: %s\n",string);
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static LRESULT CALLBACK win_window_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
CHAR fname[13];
|
|
|
|
static UINT xres = 496, yres = 384;
|
|
|
|
|
|
|
|
switch(message)
|
|
|
|
{
|
|
|
|
case WM_DESTROY:
|
|
|
|
{
|
|
|
|
PostQuitMessage(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
{
|
|
|
|
switch (wParam)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '7':
|
|
|
|
m3_config.layer_enable ^= 1;
|
|
|
|
break;
|
|
|
|
case '8':
|
|
|
|
m3_config.layer_enable ^= 2;
|
|
|
|
break;
|
|
|
|
case '9':
|
|
|
|
m3_config.layer_enable ^= 4;
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
m3_config.layer_enable ^= 8;
|
|
|
|
break;
|
|
|
|
case 0x36:
|
|
|
|
m3_config.layer_enable = 0xF;
|
|
|
|
break;
|
|
|
|
case VK_ESCAPE:
|
|
|
|
DestroyWindow(hWnd);
|
|
|
|
break;
|
|
|
|
/*case VK_F7:
|
|
|
|
strncpy(fname, m3_config.game_id, 8);
|
|
|
|
fname[8] = '\0';
|
|
|
|
strcat(fname, ".sta");
|
|
|
|
model3_save_state(fname);
|
|
|
|
break;
|
|
|
|
case VK_F8:
|
|
|
|
strncpy(fname, m3_config.game_id, 8);
|
|
|
|
fname[8] = '\0';
|
|
|
|
strcat(fname, ".sta");
|
|
|
|
model3_load_state(fname);
|
|
|
|
break;*/
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WM_KEYUP:
|
|
|
|
{
|
|
|
|
switch (wParam)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VK_F11:
|
|
|
|
{
|
|
|
|
if (m3_config.fps_limit)
|
|
|
|
{
|
|
|
|
m3_config.fps_limit = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m3_config.fps_limit = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VK_F12:
|
|
|
|
{
|
|
|
|
if (m3_config.show_fps)
|
|
|
|
{
|
|
|
|
m3_config.show_fps = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m3_config.show_fps = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|