summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/windows/winmain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/windows/winmain.cpp')
-rw-r--r--src/osd/windows/winmain.cpp1572
1 files changed, 1572 insertions, 0 deletions
diff --git a/src/osd/windows/winmain.cpp b/src/osd/windows/winmain.cpp
new file mode 100644
index 00000000000..06f1cb677fe
--- /dev/null
+++ b/src/osd/windows/winmain.cpp
@@ -0,0 +1,1572 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+// winmain.c - Win32 main program
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <commctrl.h>
+#include <mmsystem.h>
+#include <tchar.h>
+#include <io.h>
+
+// standard C headers
+#include <ctype.h>
+#include <stdarg.h>
+#include <psapi.h>
+#include <dbghelp.h>
+
+// MAME headers
+#include "emu.h"
+#include "clifront.h"
+#include "emuopts.h"
+
+// MAMEOS headers
+#include "winmain.h"
+#include "window.h"
+#include "video.h"
+#include "input.h"
+#include "output.h"
+#include "config.h"
+#include "osdepend.h"
+#include "strconv.h"
+#include "winutf8.h"
+#include "winutil.h"
+#include "debugger.h"
+#include "winfile.h"
+
+#define DEBUG_SLOW_LOCKS 0
+
+//**************************************************************************
+// MACROS
+//**************************************************************************
+
+#ifdef UNICODE
+#define UNICODE_POSTFIX "W"
+#else
+#define UNICODE_POSTFIX "A"
+#endif
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+template<typename _FunctionPtr>
+class dynamic_bind
+{
+public:
+ // constructor which looks up the function
+ dynamic_bind(const TCHAR *dll, const char *symbol)
+ : m_function(NULL)
+ {
+ HMODULE module = LoadLibrary(dll);
+ if (module != NULL)
+ m_function = reinterpret_cast<_FunctionPtr>(GetProcAddress(module, symbol));
+ }
+
+ // bool to test if the function is NULL or not
+ operator bool() const { return (m_function != NULL); }
+
+ // dereference to get the underlying pointer
+ _FunctionPtr operator *() const { return m_function; }
+
+private:
+ _FunctionPtr m_function;
+};
+
+
+class stack_walker
+{
+public:
+ stack_walker();
+
+ FPTR ip() const { return m_stackframe.AddrPC.Offset; }
+ FPTR sp() const { return m_stackframe.AddrStack.Offset; }
+ FPTR frame() const { return m_stackframe.AddrFrame.Offset; }
+
+ bool reset();
+ void reset(CONTEXT &context, HANDLE thread);
+ bool unwind();
+
+private:
+ HANDLE m_process;
+ HANDLE m_thread;
+ STACKFRAME64 m_stackframe;
+ CONTEXT m_context;
+ bool m_first;
+
+ dynamic_bind<BOOL (WINAPI *)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64)>
+ m_stack_walk_64;
+ dynamic_bind<BOOL (WINAPI *)(HANDLE, LPCTSTR, BOOL)> m_sym_initialize;
+ dynamic_bind<PVOID (WINAPI *)(HANDLE, DWORD64)> m_sym_function_table_access_64;
+ dynamic_bind<DWORD64 (WINAPI *)(HANDLE, DWORD64)> m_sym_get_module_base_64;
+ dynamic_bind<VOID (WINAPI *)(PCONTEXT)> m_rtl_capture_context;
+
+ static bool s_initialized;
+};
+
+
+class symbol_manager
+{
+public:
+ // construction/destruction
+ symbol_manager(const char *argv0);
+ ~symbol_manager();
+
+ // getters
+ FPTR last_base() const { return m_last_base; }
+
+ // core symbol lookup
+ const char *symbol_for_address(FPTR address);
+ const char *symbol_for_address(PVOID address) { return symbol_for_address(reinterpret_cast<FPTR>(address)); }
+
+ // force symbols to be cached
+ void cache_symbols() { scan_file_for_address(0, true); }
+
+ void reset_cache() { m_cache.reset(); }
+private:
+ // internal helpers
+ bool query_system_for_address(FPTR address);
+ void scan_file_for_address(FPTR address, bool create_cache);
+ bool parse_sym_line(const char *line, FPTR &address, std::string &symbol);
+ bool parse_map_line(const char *line, FPTR &address, std::string &symbol);
+ void scan_cache_for_address(FPTR address);
+ void format_symbol(const char *name, UINT32 displacement, const char *filename = NULL, int linenumber = 0);
+
+ static FPTR get_text_section_base();
+
+ struct cache_entry
+ {
+ cache_entry(FPTR address, const char *symbol) :
+ m_next(NULL), m_address(address), m_name(symbol) { }
+ cache_entry *next() const { return m_next; }
+
+ cache_entry * m_next;
+ FPTR m_address;
+ std::string m_name;
+ };
+ simple_list<cache_entry> m_cache;
+
+ std::string m_mapfile;
+ std::string m_symfile;
+ std::string m_buffer;
+ HANDLE m_process;
+ FPTR m_last_base;
+ FPTR m_text_base;
+
+ dynamic_bind<BOOL (WINAPI *)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO)> m_sym_from_addr;
+ dynamic_bind<BOOL (WINAPI *)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64)> m_sym_get_line_from_addr_64;
+};
+
+
+class sampling_profiler
+{
+public:
+ sampling_profiler(UINT32 max_seconds, UINT8 stack_depth);
+ ~sampling_profiler();
+
+ void start();
+ void stop();
+
+// void reset();
+ void print_results(symbol_manager &symbols);
+
+private:
+ static DWORD WINAPI thread_entry(LPVOID lpParameter);
+ void thread_run();
+
+ static int CLIB_DECL compare_address(const void *item1, const void *item2);
+ static int CLIB_DECL compare_frequency(const void *item1, const void *item2);
+
+ HANDLE m_target_thread;
+
+ HANDLE m_thread;
+ DWORD m_thread_id;
+ volatile bool m_thread_exit;
+
+ UINT8 m_stack_depth;
+ UINT8 m_entry_stride;
+ std::vector<FPTR> m_buffer;
+ FPTR * m_buffer_ptr;
+ FPTR * m_buffer_end;
+};
+
+//============================================================
+// winui_output_error
+//============================================================
+
+class winui_output_error : public osd_output
+{
+public:
+ virtual void output_callback(osd_output_channel channel, const char *msg, va_list args)
+ {
+ if (channel == OSD_OUTPUT_CHANNEL_ERROR)
+ {
+ char buffer[1024];
+
+ // if we are in fullscreen mode, go to windowed mode
+ if ((video_config.windowed == 0) && (win_window_list != NULL))
+ winwindow_toggle_full_screen();
+
+ vsnprintf(buffer, ARRAY_LENGTH(buffer), msg, args);
+ win_message_box_utf8(win_window_list ? win_window_list->m_hwnd : NULL, buffer, emulator_info::get_appname(), MB_OK);
+ }
+ else
+ chain_output(channel, msg, args);
+ }
+};
+
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// this line prevents globbing on the command line
+int _CRT_glob = 0;
+
+//**************************************************************************
+// LOCAL VARIABLES
+//**************************************************************************
+
+static LPTOP_LEVEL_EXCEPTION_FILTER pass_thru_filter;
+
+static HANDLE watchdog_reset_event;
+static HANDLE watchdog_exit_event;
+static HANDLE watchdog_thread;
+
+static running_machine *g_current_machine;
+
+static int timeresult = !TIMERR_NOERROR;
+static TIMECAPS timecaps;
+
+static sampling_profiler *profiler = NULL;
+static symbol_manager *symbols = NULL;
+
+bool stack_walker::s_initialized = false;
+
+
+//**************************************************************************
+// FUNCTION PROTOTYPES
+//**************************************************************************
+
+static BOOL WINAPI control_handler(DWORD type);
+static int is_double_click_start(int argc);
+static DWORD WINAPI watchdog_thread_entry(LPVOID lpParameter);
+static LONG WINAPI exception_filter(struct _EXCEPTION_POINTERS *info);
+
+
+
+//**************************************************************************
+// OPTIONS
+//**************************************************************************
+
+// struct definitions
+const options_entry windows_options::s_option_entries[] =
+{
+ // performance options
+ { NULL, NULL, OPTION_HEADER, "WINDOWS PERFORMANCE OPTIONS" },
+ { WINOPTION_PRIORITY "(-15-1)", "0", OPTION_INTEGER, "thread priority for the main game thread; range from -15 to 1" },
+ { WINOPTION_PROFILE, "0", OPTION_INTEGER, "enable profiling, specifying the stack depth to track" },
+
+ // video options
+ { NULL, NULL, OPTION_HEADER, "WINDOWS VIDEO OPTIONS" },
+ { WINOPTION_MENU, "0", OPTION_BOOLEAN, "enable menu bar if available by UI implementation" },
+
+ // DirectDraw-specific options
+ { NULL, NULL, OPTION_HEADER, "DIRECTDRAW-SPECIFIC OPTIONS" },
+ { WINOPTION_HWSTRETCH ";hws", "1", OPTION_BOOLEAN, "enable hardware stretching" },
+
+ // post-processing options
+ { NULL, NULL, OPTION_HEADER, "DIRECT3D POST-PROCESSING OPTIONS" },
+ { WINOPTION_HLSL_ENABLE";hlsl", "0", OPTION_BOOLEAN, "enable HLSL post-processing (PS3.0 required)" },
+ { WINOPTION_HLSLPATH, "hlsl", OPTION_STRING, "path to hlsl files" },
+ { WINOPTION_HLSL_PRESCALE_X, "0", OPTION_INTEGER, "HLSL pre-scale override factor for X (0 for auto)" },
+ { WINOPTION_HLSL_PRESCALE_Y, "0", OPTION_INTEGER, "HLSL pre-scale override factor for Y (0 for auto)" },
+ { WINOPTION_HLSL_PRESET";(-1-3)", "-1", OPTION_INTEGER, "HLSL preset to use (0-3)" },
+ { WINOPTION_HLSL_WRITE, NULL, OPTION_STRING, "enable HLSL AVI writing (huge disk bandwidth suggested)" },
+ { WINOPTION_HLSL_SNAP_WIDTH, "2048", OPTION_STRING, "HLSL upscaled-snapshot width" },
+ { WINOPTION_HLSL_SNAP_HEIGHT, "1536", OPTION_STRING, "HLSL upscaled-snapshot height" },
+ { WINOPTION_SHADOW_MASK_ALPHA";fs_shadwa(0.0-1.0)", "0.0", OPTION_FLOAT, "shadow mask alpha-blend value (1.0 is fully blended, 0.0 is no mask)" },
+ { WINOPTION_SHADOW_MASK_TEXTURE";fs_shadwt(0.0-1.0)", "aperture.png", OPTION_STRING, "shadow mask texture name" },
+ { WINOPTION_SHADOW_MASK_COUNT_X";fs_shadww", "6", OPTION_INTEGER, "shadow mask width, in phosphor dots" },
+ { WINOPTION_SHADOW_MASK_COUNT_Y";fs_shadwh", "6", OPTION_INTEGER, "shadow mask height, in phosphor dots" },
+ { WINOPTION_SHADOW_MASK_USIZE";fs_shadwu(0.0-1.0)", "0.1875", OPTION_FLOAT, "shadow mask texture size in U direction" },
+ { WINOPTION_SHADOW_MASK_VSIZE";fs_shadwv(0.0-1.0)", "0.1875", OPTION_FLOAT, "shadow mask texture size in V direction" },
+ { WINOPTION_SHADOW_MASK_UOFFSET";fs_shadwou(-1.0-1.0)", "0.0", OPTION_FLOAT, "shadow mask texture offset in U direction" },
+ { WINOPTION_SHADOW_MASK_VOFFSET";fs_shadwov(-1.0-1.0)", "0.0", OPTION_FLOAT, "shadow mask texture offset in V direction" },
+ { WINOPTION_CURVATURE";fs_curv(0.0-1.0)", "0.0", OPTION_FLOAT, "screen curvature amount" },
+ { WINOPTION_ROUND_CORNER";fs_rndc(0.0-1.0)", "0.0", OPTION_FLOAT, "screen round corner amount" },
+ { WINOPTION_SMOOTH_BORDER";fs_smob(0.0-1.0)", "0.0", OPTION_FLOAT, "screen smooth border amount" },
+ { WINOPTION_REFLECTION";fs_ref(0.0-1.0)", "0.0", OPTION_FLOAT, "screen reflection amount" },
+ { WINOPTION_VIGNETTING";fs_vig(0.0-1.0)", "0.0", OPTION_FLOAT, "image vignetting amount" },
+ /* Beam-related values below this line*/
+ { WINOPTION_SCANLINE_AMOUNT";fs_scanam(0.0-4.0)", "1.0", OPTION_FLOAT, "overall alpha scaling value for scanlines" },
+ { WINOPTION_SCANLINE_SCALE";fs_scansc(0.0-4.0)", "1.0", OPTION_FLOAT, "overall height scaling value for scanlines" },
+ { WINOPTION_SCANLINE_HEIGHT";fs_scanh(0.0-4.0)", "1.0", OPTION_FLOAT, "individual height scaling value for scanlines" },
+ { WINOPTION_SCANLINE_BRIGHT_SCALE";fs_scanbs(0.0-2.0)", "1.0", OPTION_FLOAT, "overall brightness scaling value for scanlines (multiplicative)" },
+ { WINOPTION_SCANLINE_BRIGHT_OFFSET";fs_scanbo(0.0-1.0)", "0.0", OPTION_FLOAT, "overall brightness offset value for scanlines (additive)" },
+ { WINOPTION_SCANLINE_OFFSET";fs_scanjt(0.0-4.0)", "0.0", OPTION_FLOAT, "overall interlace jitter scaling value for scanlines" },
+ { WINOPTION_DEFOCUS";fs_focus", "0.0,0.0", OPTION_STRING, "overall defocus value in screen-relative coords" },
+ { WINOPTION_CONVERGE_X";fs_convx", "0.3,0.0,-0.3",OPTION_STRING, "convergence in screen-relative X direction" },
+ { WINOPTION_CONVERGE_Y";fs_convy", "0.0,0.3,-0.3",OPTION_STRING, "convergence in screen-relative Y direction" },
+ { WINOPTION_RADIAL_CONVERGE_X";fs_rconvx", "0.0,0.0,0.0",OPTION_STRING, "radial convergence in screen-relative X direction" },
+ { WINOPTION_RADIAL_CONVERGE_Y";fs_rconvy", "0.0,0.0,0.0",OPTION_STRING, "radial convergence in screen-relative Y direction" },
+ /* RGB colorspace convolution below this line */
+ { WINOPTION_RED_RATIO";fs_redratio", "1.0,0.0,0.0",OPTION_STRING, "red output signal generated by input signal" },
+ { WINOPTION_GRN_RATIO";fs_grnratio", "0.0,1.0,0.0",OPTION_STRING, "green output signal generated by input signal" },
+ { WINOPTION_BLU_RATIO";fs_bluratio", "0.0,0.0,1.0",OPTION_STRING, "blue output signal generated by input signal" },
+ { WINOPTION_SATURATION";fs_sat(0.0-4.0)", "1.4", OPTION_FLOAT, "saturation scaling value" },
+ { WINOPTION_OFFSET";fs_offset", "0.0,0.0,0.0",OPTION_STRING, "signal offset value (additive)" },
+ { WINOPTION_SCALE";fs_scale", "0.95,0.95,0.95",OPTION_STRING, "signal scaling value (multiplicative)" },
+ { WINOPTION_POWER";fs_power", "0.8,0.8,0.8",OPTION_STRING, "signal power value (exponential)" },
+ { WINOPTION_FLOOR";fs_floor", "0.05,0.05,0.05",OPTION_STRING, "signal floor level" },
+ { WINOPTION_PHOSPHOR";fs_phosphor", "0.4,0.4,0.4",OPTION_STRING, "phosphorescence decay rate (0.0 is instant, 1.0 is forever)" },
+ /* NTSC simulation below this line */
+ { NULL, NULL, OPTION_HEADER, "NTSC POST-PROCESSING OPTIONS" },
+ { WINOPTION_YIQ_ENABLE";yiq", "0", OPTION_BOOLEAN, "enable YIQ-space HLSL post-processing" },
+ { WINOPTION_YIQ_CCVALUE";yiqcc", "3.59754545",OPTION_FLOAT, "Color Carrier frequency for NTSC signal processing" },
+ { WINOPTION_YIQ_AVALUE";yiqa", "0.5", OPTION_FLOAT, "A value for NTSC signal processing" },
+ { WINOPTION_YIQ_BVALUE";yiqb", "0.5", OPTION_FLOAT, "B value for NTSC signal processing" },
+ { WINOPTION_YIQ_OVALUE";yiqo", "1.570796325",OPTION_FLOAT, "Outgoing Color Carrier phase offset for NTSC signal processing" },
+ { WINOPTION_YIQ_PVALUE";yiqp", "1.0", OPTION_FLOAT, "Incoming Pixel Clock scaling value for NTSC signal processing" },
+ { WINOPTION_YIQ_NVALUE";yiqn", "1.0", OPTION_FLOAT, "Y filter notch width for NTSC signal processing" },
+ { WINOPTION_YIQ_YVALUE";yiqy", "6.0", OPTION_FLOAT, "Y filter cutoff frequency for NTSC signal processing" },
+ { WINOPTION_YIQ_IVALUE";yiqi", "1.2", OPTION_FLOAT, "I filter cutoff frequency for NTSC signal processing" },
+ { WINOPTION_YIQ_QVALUE";yiqq", "0.6", OPTION_FLOAT, "Q filter cutoff frequency for NTSC signal processing" },
+ { WINOPTION_YIQ_SCAN_TIME";yiqsc", "52.6", OPTION_FLOAT, "Horizontal scanline duration for NTSC signal processing (in usec)" },
+ { WINOPTION_YIQ_PHASE_COUNT";yiqp", "2", OPTION_INTEGER, "Phase Count value for NTSC signal processing" },
+ { WINOPTION_YIQ_SCAN_TIME";yiqsc", "52.6", OPTION_FLOAT, "Horizontal scanline duration for NTSC signal processing (in usec)" },
+ { WINOPTION_YIQ_PHASE_COUNT";yiqp", "2", OPTION_INTEGER, "Phase Count value for NTSC signal processing" },
+ /* Vector simulation below this line */
+ { NULL, NULL, OPTION_HEADER, "VECTOR POST-PROCESSING OPTIONS" },
+ { WINOPTION_VECTOR_LENGTH_SCALE";veclength", "0.8", OPTION_FLOAT, "How much length affects vector fade" },
+ { WINOPTION_VECTOR_LENGTH_RATIO";vecsize", "500.0", OPTION_FLOAT, "Vector fade length (4.0 - vectors fade the most at and above 4 pixels, etc.)" },
+ /* Bloom below this line */
+ { NULL, NULL, OPTION_HEADER, "BLOOM POST-PROCESSING OPTIONS" },
+ { WINOPTION_BLOOM_SCALE, "0.25", OPTION_FLOAT, "Intensity factor for bloom" },
+ { WINOPTION_BLOOM_OVERDRIVE, "0.0,0.0,0.0",OPTION_STRING, "Overdrive factor for bloom" },
+ { WINOPTION_BLOOM_LEVEL0_WEIGHT, "1.0", OPTION_FLOAT, "Bloom level 0 (full-size target) weight" },
+ { WINOPTION_BLOOM_LEVEL1_WEIGHT, "0.21", OPTION_FLOAT, "Bloom level 1 (half-size target) weight" },
+ { WINOPTION_BLOOM_LEVEL2_WEIGHT, "0.19", OPTION_FLOAT, "Bloom level 2 (quarter-size target) weight" },
+ { WINOPTION_BLOOM_LEVEL3_WEIGHT, "0.17", OPTION_FLOAT, "Bloom level 3 (.) weight" },
+ { WINOPTION_BLOOM_LEVEL4_WEIGHT, "0.15", OPTION_FLOAT, "Bloom level 4 (.) weight" },
+ { WINOPTION_BLOOM_LEVEL5_WEIGHT, "0.14", OPTION_FLOAT, "Bloom level 5 (.) weight" },
+ { WINOPTION_BLOOM_LEVEL6_WEIGHT, "0.13", OPTION_FLOAT, "Bloom level 6 (.) weight" },
+ { WINOPTION_BLOOM_LEVEL7_WEIGHT, "0.12", OPTION_FLOAT, "Bloom level 7 (.) weight" },
+ { WINOPTION_BLOOM_LEVEL8_WEIGHT, "0.11", OPTION_FLOAT, "Bloom level 8 (.) weight" },
+ { WINOPTION_BLOOM_LEVEL9_WEIGHT, "0.10", OPTION_FLOAT, "Bloom level 9 (.) weight" },
+ { WINOPTION_BLOOM_LEVEL10_WEIGHT, "0.09", OPTION_FLOAT, "Bloom level 10 (1x1 target) weight" },
+
+ // full screen options
+ { NULL, NULL, OPTION_HEADER, "FULL SCREEN OPTIONS" },
+ { WINOPTION_TRIPLEBUFFER ";tb", "0", OPTION_BOOLEAN, "enable triple buffering" },
+ { WINOPTION_FULLSCREENBRIGHTNESS ";fsb(0.1-2.0)", "1.0", OPTION_FLOAT, "brightness value in full screen mode" },
+ { WINOPTION_FULLSCREENCONTRAST ";fsc(0.1-2.0)", "1.0", OPTION_FLOAT, "contrast value in full screen mode" },
+ { WINOPTION_FULLSCREENGAMMA ";fsg(0.1-3.0)", "1.0", OPTION_FLOAT, "gamma value in full screen mode" },
+
+ // input options
+ { NULL, NULL, OPTION_HEADER, "INPUT DEVICE OPTIONS" },
+ { WINOPTION_GLOBAL_INPUTS ";global_inputs", "0", OPTION_BOOLEAN, "enable global inputs" },
+ { WINOPTION_DUAL_LIGHTGUN ";dual", "0", OPTION_BOOLEAN, "enable dual lightgun input" },
+
+ { NULL }
+};
+
+//**************************************************************************
+// MAIN ENTRY POINT
+//**************************************************************************
+
+
+//============================================================
+// utf8_main
+//============================================================
+
+int main(int argc, char *argv[])
+{
+ // use small output buffers on non-TTYs (i.e. pipes)
+ if (!isatty(fileno(stdout)))
+ setvbuf(stdout, (char *) NULL, _IOFBF, 64);
+ if (!isatty(fileno(stderr)))
+ setvbuf(stderr, (char *) NULL, _IOFBF, 64);
+
+ // initialize common controls
+ InitCommonControls();
+
+ // set a handler to catch ctrl-c
+ SetConsoleCtrlHandler(control_handler, TRUE);
+
+ // allocate symbols
+ symbol_manager local_symbols(argv[0]);
+ symbols = &local_symbols;
+
+ // set up exception handling
+ pass_thru_filter = SetUnhandledExceptionFilter(exception_filter);
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
+
+ // enable stack crawls for asserts
+ extern void (*s_debugger_stack_crawler)();
+ s_debugger_stack_crawler = winmain_dump_stack;
+
+
+ // parse config and cmdline options
+ DWORD result = 0;
+ {
+ windows_options options;
+ windows_osd_interface osd(options);
+ // if we're a GUI app, out errors to message boxes
+ // Initialize this after the osd interface so that we are first in the
+ // output order
+ winui_output_error winerror;
+ if (win_is_gui_application() || is_double_click_start(argc))
+ {
+ // if we are a GUI app, output errors to message boxes
+ osd_output::push(&winerror);
+ // make sure any console window that opened on our behalf is nuked
+ FreeConsole();
+ }
+ osd.register_options();
+ cli_frontend frontend(options, osd);
+ result = frontend.execute(argc, argv);
+ osd_output::pop(&winerror);
+ }
+ // free symbols
+ symbols = NULL;
+ return result;
+}
+
+
+//============================================================
+// windows_options
+//============================================================
+
+windows_options::windows_options()
+: osd_options()
+{
+ add_entries(s_option_entries);
+}
+
+
+//============================================================
+// control_handler
+//============================================================
+
+static BOOL WINAPI control_handler(DWORD type)
+{
+ // indicate to the user that we detected something
+ switch (type)
+ {
+ case CTRL_C_EVENT: fprintf(stderr, "Caught Ctrl+C"); break;
+ case CTRL_BREAK_EVENT: fprintf(stderr, "Caught Ctrl+break"); break;
+ case CTRL_CLOSE_EVENT: fprintf(stderr, "Caught console close"); break;
+ case CTRL_LOGOFF_EVENT: fprintf(stderr, "Caught logoff"); break;
+ case CTRL_SHUTDOWN_EVENT: fprintf(stderr, "Caught shutdown"); break;
+ default: fprintf(stderr, "Caught unexpected console event"); break;
+ }
+
+ // if we don't have a machine yet, or if we are handling ctrl+c/ctrl+break,
+ // just terminate hard, without throwing or handling any atexit stuff
+ if (g_current_machine == NULL || type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)
+ {
+ fprintf(stderr, ", exiting\n");
+ TerminateProcess(GetCurrentProcess(), MAMERR_FATALERROR);
+ }
+
+ // all other situations attempt to do a clean exit
+ else
+ {
+ fprintf(stderr, ", exit requested\n");
+ g_current_machine->schedule_exit();
+ }
+
+ // in all cases we handled it
+ return TRUE;
+}
+
+
+
+
+//============================================================
+// output_oslog
+//============================================================
+
+static void output_oslog(const running_machine &machine, const char *buffer)
+{
+ if (IsDebuggerPresent())
+ win_output_debug_string_utf8(buffer);
+}
+
+
+//============================================================
+// constructor
+//============================================================
+
+windows_osd_interface::windows_osd_interface(windows_options &options)
+: osd_common_t(options), m_options(options)
+{
+}
+
+
+//============================================================
+// destructor
+//============================================================
+
+windows_osd_interface::~windows_osd_interface()
+{
+}
+
+
+//============================================================
+// video_register
+//============================================================
+
+void windows_osd_interface::video_register()
+{
+ video_options_add("gdi", NULL);
+ video_options_add("ddraw", NULL);
+ video_options_add("d3d", NULL);
+ video_options_add("bgfx", NULL);
+ //video_options_add("auto", NULL); // making d3d video default one
+}
+
+//============================================================
+// init
+//============================================================
+
+void windows_osd_interface::init(running_machine &machine)
+{
+ // call our parent
+ osd_common_t::init(machine);
+
+ const char *stemp;
+ windows_options &options = downcast<windows_options &>(machine.options());
+
+ // determine if we are benchmarking, and adjust options appropriately
+ int bench = options.bench();
+ std::string error_string;
+ if (bench > 0)
+ {
+ options.set_value(OPTION_THROTTLE, false, OPTION_PRIORITY_MAXIMUM, error_string);
+ options.set_value(OSDOPTION_SOUND, "none", OPTION_PRIORITY_MAXIMUM, error_string);
+ options.set_value(OSDOPTION_VIDEO, "none", OPTION_PRIORITY_MAXIMUM, error_string);
+ options.set_value(OPTION_SECONDS_TO_RUN, bench, OPTION_PRIORITY_MAXIMUM, error_string);
+ assert(error_string.empty());
+ }
+
+ // determine if we are profiling, and adjust options appropriately
+ int profile = options.profile();
+ if (profile > 0)
+ {
+ options.set_value(OPTION_THROTTLE, false, OPTION_PRIORITY_MAXIMUM, error_string);
+ options.set_value(OSDOPTION_MULTITHREADING, false, OPTION_PRIORITY_MAXIMUM, error_string);
+ options.set_value(OSDOPTION_NUMPROCESSORS, 1, OPTION_PRIORITY_MAXIMUM, error_string);
+ assert(error_string.empty());
+ }
+
+ // thread priority
+ if (!(machine.debug_flags & DEBUG_FLAG_OSD_ENABLED))
+ SetThreadPriority(GetCurrentThread(), options.priority());
+
+ // get number of processors
+ stemp = options.numprocessors();
+
+ osd_num_processors = 0;
+
+ if (strcmp(stemp, "auto") != 0)
+ {
+ osd_num_processors = atoi(stemp);
+ if (osd_num_processors < 1)
+ {
+ osd_printf_warning("Warning: numprocessors < 1 doesn't make much sense. Assuming auto ...\n");
+ osd_num_processors = 0;
+ }
+ }
+
+ // initialize the subsystems
+ osd_common_t::init_subsystems();
+
+ // notify listeners of screen configuration
+ std::string tempstring;
+ for (win_window_info *info = win_window_list; info != NULL; info = info->m_next)
+ {
+ strprintf(tempstring, "Orientation(%s)", info->m_monitor->devicename());
+ output_set_value(tempstring.c_str(), info->m_targetorient);
+ }
+
+ // hook up the debugger log
+ if (options.oslog())
+ machine.add_logerror_callback(output_oslog);
+
+ // crank up the multimedia timer resolution to its max
+ // this gives the system much finer timeslices
+ timeresult = timeGetDevCaps(&timecaps, sizeof(timecaps));
+ if (timeresult == TIMERR_NOERROR)
+ timeBeginPeriod(timecaps.wPeriodMin);
+
+ // if a watchdog thread is requested, create one
+ int watchdog = options.watchdog();
+ if (watchdog != 0)
+ {
+ watchdog_reset_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ assert_always(watchdog_reset_event != NULL, "Failed to create watchdog reset event");
+ watchdog_exit_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ assert_always(watchdog_exit_event != NULL, "Failed to create watchdog exit event");
+ watchdog_thread = CreateThread(NULL, 0, watchdog_thread_entry, (LPVOID)(FPTR)watchdog, 0, NULL);
+ assert_always(watchdog_thread != NULL, "Failed to create watchdog thread");
+ }
+
+ // create and start the profiler
+ if (profile > 0)
+ {
+ profiler = global_alloc(sampling_profiler(1000, profile - 1));
+ profiler->start();
+ }
+
+ // initialize sockets
+ win_init_sockets();
+
+ // note the existence of a machine
+ g_current_machine = &machine;
+}
+
+
+//============================================================
+// osd_exit
+//============================================================
+
+void windows_osd_interface::osd_exit()
+{
+ // no longer have a machine
+ g_current_machine = NULL;
+
+ // cleanup sockets
+ win_cleanup_sockets();
+
+ osd_common_t::osd_exit();
+
+ // take down the watchdog thread if it exists
+ if (watchdog_thread != NULL)
+ {
+ SetEvent(watchdog_exit_event);
+ WaitForSingleObject(watchdog_thread, INFINITE);
+ CloseHandle(watchdog_reset_event);
+ CloseHandle(watchdog_exit_event);
+ CloseHandle(watchdog_thread);
+ watchdog_reset_event = NULL;
+ watchdog_exit_event = NULL;
+ watchdog_thread = NULL;
+ }
+
+ // stop the profiler
+ if (profiler != NULL)
+ {
+ profiler->stop();
+ profiler->print_results(*symbols);
+ global_free(profiler);
+ }
+
+ // restore the timer resolution
+ if (timeresult == TIMERR_NOERROR)
+ timeEndPeriod(timecaps.wPeriodMin);
+
+ // one last pass at events
+ winwindow_process_events(machine(), 0, 0);
+}
+
+//============================================================
+// winmain_dump_stack
+//============================================================
+
+void winmain_dump_stack()
+{
+ // set up the stack walker
+ stack_walker walker;
+ if (!walker.reset())
+ return;
+
+ // walk the stack
+ while (walker.unwind())
+ fprintf(stderr, " %p: %p%s\n", (void *)walker.frame(), (void *)walker.ip(), (symbols == NULL) ? "" : symbols->symbol_for_address(walker.ip()));
+}
+
+
+//============================================================
+// check_for_double_click_start
+//============================================================
+
+static int is_double_click_start(int argc)
+{
+ STARTUPINFO startup_info = { sizeof(STARTUPINFO) };
+
+ // determine our startup information
+ GetStartupInfo(&startup_info);
+
+ // try to determine if MAME was simply double-clicked
+ return (argc <= 1 && startup_info.dwFlags && !(startup_info.dwFlags & STARTF_USESTDHANDLES));
+}
+
+
+//============================================================
+// watchdog_thread_entry
+//============================================================
+
+static DWORD WINAPI watchdog_thread_entry(LPVOID lpParameter)
+{
+ DWORD timeout = (int)(FPTR)lpParameter * 1000;
+
+ while (TRUE)
+ {
+ HANDLE handle_list[2];
+ DWORD wait_result;
+
+ // wait for either a reset or an exit, or a timeout
+ handle_list[0] = watchdog_reset_event;
+ handle_list[1] = watchdog_exit_event;
+ wait_result = WaitForMultipleObjects(2, handle_list, FALSE, timeout);
+
+ // on a reset, just loop around and re-wait
+ if (wait_result == WAIT_OBJECT_0 + 0)
+ continue;
+
+ // on an exit, break out
+ if (wait_result == WAIT_OBJECT_0 + 1)
+ break;
+
+ // on a timeout, kill the process
+ if (wait_result == WAIT_TIMEOUT)
+ {
+ fprintf(stderr, "Terminating due to watchdog timeout\n");
+ fflush(stderr);
+ TerminateProcess(GetCurrentProcess(), -1);
+ }
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+//============================================================
+// winmain_watchdog_ping
+//============================================================
+
+void winmain_watchdog_ping(void)
+{
+ // if we have a watchdog, reset it
+ if (watchdog_reset_event != NULL)
+ SetEvent(watchdog_reset_event);
+}
+
+
+//============================================================
+// exception_filter
+//============================================================
+
+static LONG WINAPI exception_filter(struct _EXCEPTION_POINTERS *info)
+{
+ static const struct
+ {
+ DWORD code;
+ const char *string;
+ } exception_table[] =
+ {
+ { EXCEPTION_ACCESS_VIOLATION, "ACCESS VIOLATION" },
+ { EXCEPTION_DATATYPE_MISALIGNMENT, "DATATYPE MISALIGNMENT" },
+ { EXCEPTION_BREAKPOINT, "BREAKPOINT" },
+ { EXCEPTION_SINGLE_STEP, "SINGLE STEP" },
+ { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "ARRAY BOUNDS EXCEEDED" },
+ { EXCEPTION_FLT_DENORMAL_OPERAND, "FLOAT DENORMAL OPERAND" },
+ { EXCEPTION_FLT_DIVIDE_BY_ZERO, "FLOAT DIVIDE BY ZERO" },
+ { EXCEPTION_FLT_INEXACT_RESULT, "FLOAT INEXACT RESULT" },
+ { EXCEPTION_FLT_INVALID_OPERATION, "FLOAT INVALID OPERATION" },
+ { EXCEPTION_FLT_OVERFLOW, "FLOAT OVERFLOW" },
+ { EXCEPTION_FLT_STACK_CHECK, "FLOAT STACK CHECK" },
+ { EXCEPTION_FLT_UNDERFLOW, "FLOAT UNDERFLOW" },
+ { EXCEPTION_INT_DIVIDE_BY_ZERO, "INTEGER DIVIDE BY ZERO" },
+ { EXCEPTION_INT_OVERFLOW, "INTEGER OVERFLOW" },
+ { EXCEPTION_PRIV_INSTRUCTION, "PRIVILEGED INSTRUCTION" },
+ { EXCEPTION_IN_PAGE_ERROR, "IN PAGE ERROR" },
+ { EXCEPTION_ILLEGAL_INSTRUCTION, "ILLEGAL INSTRUCTION" },
+ { EXCEPTION_NONCONTINUABLE_EXCEPTION,"NONCONTINUABLE EXCEPTION" },
+ { EXCEPTION_STACK_OVERFLOW, "STACK OVERFLOW" },
+ { EXCEPTION_INVALID_DISPOSITION, "INVALID DISPOSITION" },
+ { EXCEPTION_GUARD_PAGE, "GUARD PAGE VIOLATION" },
+ { EXCEPTION_INVALID_HANDLE, "INVALID HANDLE" },
+ { 0, "UNKNOWN EXCEPTION" }
+ };
+ static int already_hit = 0;
+ int i;
+
+ // if we're hitting this recursively, just exit
+ if (already_hit)
+ return EXCEPTION_CONTINUE_SEARCH;
+ already_hit = 1;
+
+ // flush any debugging traces that were live
+ debugger_flush_all_traces_on_abnormal_exit();
+
+ // find our man
+ for (i = 0; exception_table[i].code != 0; i++)
+ if (info->ExceptionRecord->ExceptionCode == exception_table[i].code)
+ break;
+
+ // print the exception type and address
+ fprintf(stderr, "\n-----------------------------------------------------\n");
+ fprintf(stderr, "Exception at EIP=%p%s: %s\n", info->ExceptionRecord->ExceptionAddress,
+ symbols->symbol_for_address((FPTR)info->ExceptionRecord->ExceptionAddress), exception_table[i].string);
+
+ // for access violations, print more info
+ if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ fprintf(stderr, "While attempting to %s memory at %p\n",
+ info->ExceptionRecord->ExceptionInformation[0] ? "write" : "read",
+ (void *)info->ExceptionRecord->ExceptionInformation[1]);
+
+ // print the state of the CPU
+ fprintf(stderr, "-----------------------------------------------------\n");
+#ifdef PTR64
+ fprintf(stderr, "RAX=%p RBX=%p RCX=%p RDX=%p\n",
+ (void *)info->ContextRecord->Rax,
+ (void *)info->ContextRecord->Rbx,
+ (void *)info->ContextRecord->Rcx,
+ (void *)info->ContextRecord->Rdx);
+ fprintf(stderr, "RSI=%p RDI=%p RBP=%p RSP=%p\n",
+ (void *)info->ContextRecord->Rsi,
+ (void *)info->ContextRecord->Rdi,
+ (void *)info->ContextRecord->Rbp,
+ (void *)info->ContextRecord->Rsp);
+ fprintf(stderr, " R8=%p R9=%p R10=%p R11=%p\n",
+ (void *)info->ContextRecord->R8,
+ (void *)info->ContextRecord->R9,
+ (void *)info->ContextRecord->R10,
+ (void *)info->ContextRecord->R11);
+ fprintf(stderr, "R12=%p R13=%p R14=%p R15=%p\n",
+ (void *)info->ContextRecord->R12,
+ (void *)info->ContextRecord->R13,
+ (void *)info->ContextRecord->R14,
+ (void *)info->ContextRecord->R15);
+#else
+ fprintf(stderr, "EAX=%p EBX=%p ECX=%p EDX=%p\n",
+ (void *)info->ContextRecord->Eax,
+ (void *)info->ContextRecord->Ebx,
+ (void *)info->ContextRecord->Ecx,
+ (void *)info->ContextRecord->Edx);
+ fprintf(stderr, "ESI=%p EDI=%p EBP=%p ESP=%p\n",
+ (void *)info->ContextRecord->Esi,
+ (void *)info->ContextRecord->Edi,
+ (void *)info->ContextRecord->Ebp,
+ (void *)info->ContextRecord->Esp);
+#endif
+
+ stack_walker walker;
+ walker.reset(*info->ContextRecord, GetCurrentThread());
+
+ // reprint the actual exception address
+ fprintf(stderr, "-----------------------------------------------------\n");
+ fprintf(stderr, "Stack crawl:\n");
+
+ // walk the stack
+ while (walker.unwind())
+ fprintf(stderr, " %p: %p%s\n", (void *)walker.frame(), (void *)walker.ip(), (symbols == NULL) ? "" : symbols->symbol_for_address(walker.ip()));
+
+ // flush stderr, so the data is actually written when output is being redirected
+ fflush(stderr);
+
+ // exit
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+//**************************************************************************
+// STACK WALKER
+//**************************************************************************
+
+//-------------------------------------------------
+// stack_walker - constructor
+//-------------------------------------------------
+
+stack_walker::stack_walker()
+ : m_process(GetCurrentProcess()),
+ m_thread(GetCurrentThread()),
+ m_first(true),
+ m_stack_walk_64(TEXT("dbghelp.dll"), "StackWalk64"),
+ m_sym_initialize(TEXT("dbghelp.dll"), "SymInitialize"),
+ m_sym_function_table_access_64(TEXT("dbghelp.dll"), "SymFunctionTableAccess64"),
+ m_sym_get_module_base_64(TEXT("dbghelp.dll"), "SymGetModuleBase64"),
+ m_rtl_capture_context(TEXT("kernel32.dll"), "RtlCaptureContext")
+{
+ // zap the structs
+ memset(&m_stackframe, 0, sizeof(m_stackframe));
+ memset(&m_context, 0, sizeof(m_context));
+
+ // initialize the symbols
+ if (!s_initialized && m_sym_initialize && m_stack_walk_64 && m_sym_function_table_access_64 && m_sym_get_module_base_64)
+ {
+ (*m_sym_initialize)(m_process, NULL, TRUE);
+ s_initialized = true;
+ }
+}
+
+
+//-------------------------------------------------
+// reset - set up a new context
+//-------------------------------------------------
+
+bool stack_walker::reset()
+{
+ // set up the initial state
+ if (!m_rtl_capture_context)
+ return false;
+ (*m_rtl_capture_context)(&m_context);
+ m_thread = GetCurrentThread();
+ m_first = true;
+
+ // initialize the stackframe
+ memset(&m_stackframe, 0, sizeof(m_stackframe));
+ m_stackframe.AddrPC.Mode = AddrModeFlat;
+ m_stackframe.AddrFrame.Mode = AddrModeFlat;
+ m_stackframe.AddrStack.Mode = AddrModeFlat;
+
+ // pull architecture-specific fields from the context
+#ifdef PTR64
+ m_stackframe.AddrPC.Offset = m_context.Rip;
+ m_stackframe.AddrFrame.Offset = m_context.Rsp;
+ m_stackframe.AddrStack.Offset = m_context.Rsp;
+#else
+ m_stackframe.AddrPC.Offset = m_context.Eip;
+ m_stackframe.AddrFrame.Offset = m_context.Ebp;
+ m_stackframe.AddrStack.Offset = m_context.Esp;
+#endif
+ return true;
+}
+
+void stack_walker::reset(CONTEXT &initial, HANDLE thread)
+{
+ // set up the initial state
+ m_context = initial;
+ m_thread = thread;
+ m_first = true;
+
+ // initialize the stackframe
+ memset(&m_stackframe, 0, sizeof(m_stackframe));
+ m_stackframe.AddrPC.Mode = AddrModeFlat;
+ m_stackframe.AddrFrame.Mode = AddrModeFlat;
+ m_stackframe.AddrStack.Mode = AddrModeFlat;
+
+ // pull architecture-specific fields from the context
+#ifdef PTR64
+ m_stackframe.AddrPC.Offset = m_context.Rip;
+ m_stackframe.AddrFrame.Offset = m_context.Rsp;
+ m_stackframe.AddrStack.Offset = m_context.Rsp;
+#else
+ m_stackframe.AddrPC.Offset = m_context.Eip;
+ m_stackframe.AddrFrame.Offset = m_context.Ebp;
+ m_stackframe.AddrStack.Offset = m_context.Esp;
+#endif
+}
+
+
+//-------------------------------------------------
+// unwind - unwind a single level
+//-------------------------------------------------
+
+bool stack_walker::unwind()
+{
+ // if we were able to initialize, then we have everything we need
+ if (s_initialized)
+ {
+#ifdef PTR64
+ return (*m_stack_walk_64)(IMAGE_FILE_MACHINE_AMD64, m_process, m_thread, &m_stackframe, &m_context, NULL, *m_sym_function_table_access_64, *m_sym_get_module_base_64, NULL);
+#else
+ return (*m_stack_walk_64)(IMAGE_FILE_MACHINE_I386, m_process, m_thread, &m_stackframe, &m_context, NULL, *m_sym_function_table_access_64, *m_sym_get_module_base_64, NULL);
+#endif
+ }
+
+ // otherwise, fake the first unwind, which will just return info from the context
+ else
+ {
+ bool result = m_first;
+ m_first = false;
+ return result;
+ }
+}
+
+
+
+//**************************************************************************
+// SYMBOL MANAGER
+//**************************************************************************
+
+//-------------------------------------------------
+// symbol_manager - constructor
+//-------------------------------------------------
+
+symbol_manager::symbol_manager(const char *argv0)
+ : m_mapfile(argv0),
+ m_symfile(argv0),
+ m_process(GetCurrentProcess()),
+ m_last_base(0),
+ m_text_base(0),
+ m_sym_from_addr(TEXT("dbghelp.dll"), "SymFromAddr"),
+ m_sym_get_line_from_addr_64(TEXT("dbghelp.dll"), "SymGetLineFromAddr64")
+{
+#ifdef __GNUC__
+ // compute the name of the mapfile
+ int extoffs = m_mapfile.find_last_of('.');
+ if (extoffs != -1)
+ m_mapfile.substr(0, extoffs);
+ m_mapfile.append(".map");
+
+ // and the name of the symfile
+ extoffs = m_symfile.find_last_of('.');
+ if (extoffs != -1)
+ m_symfile = m_symfile.substr(0, extoffs);
+ m_symfile.append(".sym");
+
+ // figure out the base of the .text section
+ m_text_base = get_text_section_base();
+#endif
+
+ // expand the buffer to be decently large up front
+ strprintf(m_buffer,"%500s", "");
+}
+
+
+//-------------------------------------------------
+// ~symbol_manager - destructor
+//-------------------------------------------------
+
+symbol_manager::~symbol_manager()
+{
+}
+
+
+//-------------------------------------------------
+// symbol_for_address - return a symbol by looking
+// it up either in the cache or by scanning the
+// file
+//-------------------------------------------------
+
+const char *symbol_manager::symbol_for_address(FPTR address)
+{
+ // default the buffer
+ m_buffer.assign(" (not found)");
+ m_last_base = 0;
+
+ // first try to do it using system APIs
+ if (!query_system_for_address(address))
+ {
+ // if that fails, scan the cache if we have one
+ if (m_cache.first() != NULL)
+ scan_cache_for_address(address);
+
+ // or else try to open a sym/map file and find it there
+ else
+ scan_file_for_address(address, false);
+ }
+ return m_buffer.c_str();
+}
+
+
+//-------------------------------------------------
+// query_system_for_address - ask the system to
+// look up our address
+//-------------------------------------------------
+
+bool symbol_manager::query_system_for_address(FPTR address)
+{
+ // need at least the sym_from_addr API
+ if (!m_sym_from_addr)
+ return false;
+
+ BYTE info_buffer[sizeof(SYMBOL_INFO) + 256] = { 0 };
+ SYMBOL_INFO &info = *reinterpret_cast<SYMBOL_INFO *>(&info_buffer[0]);
+ DWORD64 displacement;
+
+ // even through the struct says TCHAR, we actually get back an ANSI string here
+ info.SizeOfStruct = sizeof(info);
+ info.MaxNameLen = sizeof(info_buffer) - sizeof(info);
+ if ((*m_sym_from_addr)(m_process, address, &displacement, &info))
+ {
+ // try to get source info as well; again we are returned an ANSI string
+ IMAGEHLP_LINE64 lineinfo = { sizeof(lineinfo) };
+ DWORD linedisp;
+ if (m_sym_get_line_from_addr_64 && (*m_sym_get_line_from_addr_64)(m_process, address, &linedisp, &lineinfo))
+ format_symbol(info.Name, displacement, lineinfo.FileName, lineinfo.LineNumber);
+ else
+ format_symbol(info.Name, displacement);
+
+ // set the last base
+ m_last_base = address - displacement;
+ return true;
+ }
+ return false;
+}
+
+
+//-------------------------------------------------
+// scan_file_for_address - walk either the map
+// or symbol files and find the best match for
+// the given address, optionally creating a cache
+// along the way
+//-------------------------------------------------
+
+void symbol_manager::scan_file_for_address(FPTR address, bool create_cache)
+{
+ bool is_symfile = false;
+ FILE *srcfile = NULL;
+
+#ifdef __GNUC__
+ // see if we have a symbol file (gcc only)
+ srcfile = fopen(m_symfile.c_str(), "r");
+ is_symfile = (srcfile != NULL);
+#endif
+
+ // if not, see if we have a map file
+ if (srcfile == NULL)
+ srcfile = fopen(m_mapfile.c_str(), "r");
+
+ // if not, fail
+ if (srcfile == NULL)
+ return;
+
+ // reset the best info
+ std::string best_symbol;
+ FPTR best_addr = 0;
+
+ // parse the file, looking for valid entries
+ std::string symbol;
+ char line[1024];
+ while (fgets(line, sizeof(line) - 1, srcfile))
+ {
+ // parse the line looking for an interesting symbol
+ FPTR addr = 0;
+ bool valid = is_symfile ? parse_sym_line(line, addr, symbol) : parse_map_line(line, addr, symbol);
+
+ // if we got one, see if this is the best
+ if (valid)
+ {
+ // if this is the best one so far, remember it
+ if (addr <= address && addr > best_addr)
+ {
+ best_addr = addr;
+ best_symbol = symbol;
+ }
+
+ // also create a cache entry if we can
+ if (create_cache)
+ m_cache.append(*global_alloc(cache_entry(addr, symbol.c_str())));
+ }
+ }
+
+ // close the file
+ fclose(srcfile);
+
+ // format the symbol and remember the last base
+ format_symbol(best_symbol.c_str(), address - best_addr);
+ m_last_base = best_addr;
+}
+
+
+//-------------------------------------------------
+// scan_cache_for_address - walk the cache to
+// find the best match for the given address
+//-------------------------------------------------
+
+void symbol_manager::scan_cache_for_address(FPTR address)
+{
+ // reset the best info
+ std::string best_symbol;
+ FPTR best_addr = 0;
+
+ // walk the cache, looking for valid entries
+ for (cache_entry *entry = m_cache.first(); entry != NULL; entry = entry->next())
+
+ // if this is the best one so far, remember it
+ if (entry->m_address <= address && entry->m_address > best_addr)
+ {
+ best_addr = entry->m_address;
+ best_symbol = entry->m_name;
+ }
+
+ // format the symbol and remember the last base
+ format_symbol(best_symbol.c_str(), address - best_addr);
+ m_last_base = best_addr;
+}
+
+
+//-------------------------------------------------
+// parse_sym_line - parse a line from a sym file
+// which is just the output of objdump
+//-------------------------------------------------
+
+bool symbol_manager::parse_sym_line(const char *line, FPTR &address, std::string &symbol)
+{
+#ifdef __GNUC__
+/*
+ 32-bit gcc symbol line:
+[271778](sec 1)(fl 0x00)(ty 20)(scl 3) (nx 0) 0x007df675 line_to_symbol(char const*, unsigned int&, bool)
+
+ 64-bit gcc symbol line:
+[271775](sec 1)(fl 0x00)(ty 20)(scl 3) (nx 0) 0x00000000008dd1e9 line_to_symbol(char const*, unsigned long long&, bool)
+*/
+
+ // first look for a (ty) entry
+ const char *type = strstr(line, "(ty 20)");
+ if (type == NULL)
+ return false;
+
+ // scan forward in the line to find the address
+ bool in_parens = false;
+ for (const char *chptr = type; *chptr != 0; chptr++)
+ {
+ // track open/close parentheses
+ if (*chptr == '(')
+ in_parens = true;
+ else if (*chptr == ')')
+ in_parens = false;
+
+ // otherwise, look for an 0x address
+ else if (!in_parens && *chptr == '0' && chptr[1] == 'x')
+ {
+ // make sure we can get an address
+ void *temp;
+ if (sscanf(chptr, "0x%p", &temp) != 1)
+ return false;
+ address = m_text_base + reinterpret_cast<FPTR>(temp);
+
+ // skip forward until we're past the space
+ while (*chptr != 0 && !isspace(*chptr))
+ chptr++;
+
+ // extract the symbol name
+ strtrimspace(symbol.assign(chptr));
+ return (symbol.length() > 0);
+ }
+ }
+#endif
+ return false;
+}
+
+
+//-------------------------------------------------
+// parse_map_line - parse a line from a linker-
+// generated map file
+//-------------------------------------------------
+
+bool symbol_manager::parse_map_line(const char *line, FPTR &address, std::string &symbol)
+{
+#ifdef __GNUC__
+/*
+ 32-bit gcc map line:
+ 0x0089cb00 nbmj9195_palette_r(_address_space const*, unsigned int)
+
+ 64-bit gcc map line:
+ 0x0000000000961afc nbmj9195_palette_r(_address_space const*, unsigned int)
+*/
+
+ // find a matching start
+ if (strncmp(line, " 0x", 18) == 0)
+ {
+ // make sure we can get an address
+ void *temp;
+ if (sscanf(&line[16], "0x%p", &temp) != 1)
+ return false;
+ address = reinterpret_cast<FPTR>(temp);
+
+ // skip forward until we're past the space
+ const char *chptr = &line[16];
+ while (*chptr != 0 && !isspace(*chptr))
+ chptr++;
+
+ // extract the symbol name
+ strtrimspace(symbol.assign(chptr));
+ return (symbol.length() > 0);
+ }
+#endif
+ return false;
+}
+
+
+//-------------------------------------------------
+// format_symbol - common symbol formatting
+//-------------------------------------------------
+
+void symbol_manager::format_symbol(const char *name, UINT32 displacement, const char *filename, int linenumber)
+{
+ // start with the address and offset
+ strprintf(m_buffer, " (%s", name);
+ if (displacement != 0)
+ strcatprintf(m_buffer, "+0x%04x", (UINT32)displacement);
+
+ // append file/line if present
+ if (filename != NULL)
+ strcatprintf(m_buffer, ", %s:%d", filename, linenumber);
+
+ // close up the string
+ m_buffer.append(")");
+}
+
+
+//-------------------------------------------------
+// get_text_section_base - figure out the base
+// of the .text section
+//-------------------------------------------------
+
+FPTR symbol_manager::get_text_section_base()
+{
+ dynamic_bind<PIMAGE_SECTION_HEADER (WINAPI *)(PIMAGE_NT_HEADERS, PVOID, ULONG)> image_rva_to_section(TEXT("dbghelp.dll"), "ImageRvaToSection");
+ dynamic_bind<PIMAGE_NT_HEADERS (WINAPI *)(PVOID)> image_nt_header(TEXT("dbghelp.dll"), "ImageNtHeader");
+
+ // start with the image base
+ PVOID base = reinterpret_cast<PVOID>(GetModuleHandleUni());
+ assert(base != NULL);
+
+ // make sure we have the functions we need
+ if (image_nt_header && image_rva_to_section)
+ {
+ // get the NT header
+ PIMAGE_NT_HEADERS headers = (*image_nt_header)(base);
+ assert(headers != NULL);
+
+ // look ourself up (assuming we are in the .text section)
+ PIMAGE_SECTION_HEADER section = (*image_rva_to_section)(headers, base, reinterpret_cast<FPTR>(get_text_section_base) - reinterpret_cast<FPTR>(base));
+ if (section != NULL)
+ return reinterpret_cast<FPTR>(base) + section->VirtualAddress;
+ }
+
+ // fallback to returning the image base (wrong)
+ return reinterpret_cast<FPTR>(base);
+}
+
+
+
+//**************************************************************************
+// SAMPLING PROFILER
+//**************************************************************************
+
+//-------------------------------------------------
+// sampling_profiler - constructor
+//-------------------------------------------------
+
+sampling_profiler::sampling_profiler(UINT32 max_seconds, UINT8 stack_depth = 0)
+ : m_thread(NULL),
+ m_thread_id(0),
+ m_thread_exit(false),
+ m_stack_depth(stack_depth),
+ m_entry_stride(stack_depth + 2),
+ m_buffer(max_seconds * 1000 * m_entry_stride),
+ m_buffer_ptr(&m_buffer[0]),
+ m_buffer_end(&m_buffer[0] + max_seconds * 1000 * m_entry_stride)
+{
+}
+
+
+//-------------------------------------------------
+// sampling_profiler - destructor
+//-------------------------------------------------
+
+sampling_profiler::~sampling_profiler()
+{
+}
+
+
+//-------------------------------------------------
+// start - begin gathering profiling information
+//-------------------------------------------------
+
+void sampling_profiler::start()
+{
+ // do the dance to get a handle to ourself
+ BOOL result = DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &m_target_thread,
+ THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, FALSE, 0);
+ assert_always(result, "Failed to get thread handle for main thread");
+
+ // reset the exit flag
+ m_thread_exit = false;
+
+ // start the thread
+ m_thread = CreateThread(NULL, 0, thread_entry, (LPVOID)this, 0, &m_thread_id);
+ assert_always(m_thread != NULL, "Failed to create profiler thread\n");
+
+ // max out the priority
+ SetThreadPriority(m_thread, THREAD_PRIORITY_TIME_CRITICAL);
+}
+
+
+//-------------------------------------------------
+// stop - stop gathering profiling information
+//-------------------------------------------------
+
+void sampling_profiler::stop()
+{
+ // set the flag and wait a couple of seconds (max)
+ m_thread_exit = true;
+ WaitForSingleObject(m_thread, 2000);
+
+ // regardless, close the handle
+ CloseHandle(m_thread);
+}
+
+
+//-------------------------------------------------
+// compare_address - compare two entries by their
+// bucket address
+//-------------------------------------------------
+
+int CLIB_DECL sampling_profiler::compare_address(const void *item1, const void *item2)
+{
+ const FPTR *entry1 = reinterpret_cast<const FPTR *>(item1);
+ const FPTR *entry2 = reinterpret_cast<const FPTR *>(item2);
+ int mincount = MIN(entry1[0], entry2[0]);
+
+ // sort in order of: bucket, caller, caller's caller, etc.
+ for (int index = 1; index <= mincount; index++)
+ if (entry1[index] != entry2[index])
+ return entry1[index] - entry2[index];
+
+ // if we match to the end, sort by the depth of the stack
+ return entry1[0] - entry2[0];
+}
+
+
+//-------------------------------------------------
+// compare_frequency - compare two entries by
+// their frequency of occurrence
+//-------------------------------------------------
+
+int CLIB_DECL sampling_profiler::compare_frequency(const void *item1, const void *item2)
+{
+ const FPTR *entry1 = reinterpret_cast<const FPTR *>(item1);
+ const FPTR *entry2 = reinterpret_cast<const FPTR *>(item2);
+
+ // sort by frequency, then by address
+ if (entry1[0] != entry2[0])
+ return entry2[0] - entry1[0];
+ return entry1[1] - entry2[1];
+}
+
+
+//-------------------------------------------------
+// print_results - output the results
+//-------------------------------------------------
+
+void sampling_profiler::print_results(symbol_manager &symbols)
+{
+ // cache the symbols
+ symbols.cache_symbols();
+
+ // step 1: find the base of each entry
+ for (FPTR *current = &m_buffer[0]; current < m_buffer_ptr; current += m_entry_stride)
+ {
+ assert(current[0] >= 1 && current[0] < m_entry_stride);
+
+ // convert the sampled PC to its function base as a bucket
+ symbols.symbol_for_address(current[1]);
+ current[1] = symbols.last_base();
+ }
+
+ // step 2: sort the results
+ qsort(&m_buffer[0], (m_buffer_ptr - &m_buffer[0]) / m_entry_stride, m_entry_stride * sizeof(FPTR), compare_address);
+
+ // step 3: count and collapse unique entries
+ UINT32 total_count = 0;
+ for (FPTR *current = &m_buffer[0]; current < m_buffer_ptr; )
+ {
+ int count = 1;
+ FPTR *scan;
+ for (scan = current + m_entry_stride; scan < m_buffer_ptr; scan += m_entry_stride)
+ {
+ if (compare_address(current, scan) != 0)
+ break;
+ scan[0] = 0;
+ count++;
+ }
+ current[0] = count;
+ total_count += count;
+ current = scan;
+ }
+
+ // step 4: sort the results again, this time by frequency
+ qsort(&m_buffer[0], (m_buffer_ptr - &m_buffer[0]) / m_entry_stride, m_entry_stride * sizeof(FPTR), compare_frequency);
+
+ // step 5: print the results
+ UINT32 num_printed = 0;
+ for (FPTR *current = &m_buffer[0]; current < m_buffer_ptr && num_printed < 30; current += m_entry_stride)
+ {
+ // once we hit 0 frequency, we're done
+ if (current[0] == 0)
+ break;
+
+ // output the result
+ printf("%4.1f%% - %6d : %p%s\n", (double)current[0] * 100.0 / (double)total_count, (UINT32)current[0], reinterpret_cast<void *>(current[1]), symbols.symbol_for_address(current[1]));
+ for (int index = 2; index < m_entry_stride; index++)
+ {
+ if (current[index] == 0)
+ break;
+ printf(" %p%s\n", reinterpret_cast<void *>(current[index]), symbols.symbol_for_address(current[index]));
+ }
+ printf("\n");
+ num_printed++;
+ }
+ symbols.reset_cache();
+}
+
+
+//-------------------------------------------------
+// thread_entry - thread entry stub
+//-------------------------------------------------
+
+DWORD WINAPI sampling_profiler::thread_entry(LPVOID lpParameter)
+{
+ reinterpret_cast<sampling_profiler *>(lpParameter)->thread_run();
+ return 0;
+}
+
+
+//-------------------------------------------------
+// thread_run - sampling thread
+//-------------------------------------------------
+
+void sampling_profiler::thread_run()
+{
+ CONTEXT context;
+ memset(&context, 0, sizeof(context));
+
+ // loop until done
+ stack_walker walker;
+ while (!m_thread_exit && m_buffer_ptr < m_buffer_end)
+ {
+ // pause the main thread and get its context
+ SuspendThread(m_target_thread);
+ context.ContextFlags = CONTEXT_FULL;
+ GetThreadContext(m_target_thread, &context);
+
+ // first entry is a count
+ FPTR *count = m_buffer_ptr++;
+ *count = 0;
+
+ // iterate over the frames until we run out or hit an error
+ walker.reset(context, m_target_thread);
+ int frame;
+ for (frame = 0; frame <= m_stack_depth && walker.unwind(); frame++)
+ {
+ *m_buffer_ptr++ = walker.ip();
+ *count += 1;
+ }
+
+ // fill in any missing parts with NULLs
+ for (; frame <= m_stack_depth; frame++)
+ *m_buffer_ptr++ = 0;
+
+ // resume the thread
+ ResumeThread(m_target_thread);
+
+ // sleep for 1ms
+ Sleep(1);
+ }
+}