// license:BSD-3-Clause
// copyright-holders:Aaron Giles
//============================================================
//
//  debugwin.c - Win32 debug window handling
//
//============================================================

// standard windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#include <commdlg.h>
#ifdef _MSC_VER
#include <zmouse.h>
#endif

// MAME headers
#include "emu.h"
#include "uiinput.h"
#include "imagedev/cassette.h"
#include "debugger.h"
#include "debug/debugvw.h"
#include "debug/dvdisasm.h"
#include "debug/dvmemory.h"
#include "debug/dvstate.h"
#include "debug/debugvw.h"
#include "debug/debugcon.h"
#include "debug/debugcpu.h"

// MAMEOS headers
#include "winmain.h"
#include "window.h"
#include "../../windows/input.h"
#include "strconv.h"
#include "winutf8.h"

#include "debugwin.h"

const osd_debugger_type OSD_DEBUGGER_WINDOWS = &osd_debugger_creator<debugger_windows>;

//============================================================
//  PARAMETERS
//============================================================

#define MAX_VIEWS               4
#define EDGE_WIDTH              3
#define MAX_EDIT_STRING         256
#define HISTORY_LENGTH          20
#define MAX_OTHER_WND           4

// debugger window styles
#define DEBUG_WINDOW_STYLE      (WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN) & (~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX)
#define DEBUG_WINDOW_STYLE_EX   0

// debugger view styles
#define DEBUG_VIEW_STYLE        WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
#define DEBUG_VIEW_STYLE_EX     0

// edit box styles
#define EDIT_BOX_STYLE          WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL
#define EDIT_BOX_STYLE_EX       0

// combo box styles
#define COMBO_BOX_STYLE         WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL
#define COMBO_BOX_STYLE_EX      0

// horizontal scroll bar styles
#define HSCROLL_STYLE           WS_CHILD | WS_VISIBLE | SBS_HORZ
#define HSCROLL_STYLE_EX        0

// vertical scroll bar styles
#define VSCROLL_STYLE           WS_CHILD | WS_VISIBLE | SBS_VERT
#define VSCROLL_STYLE_EX        0


enum
{
	ID_NEW_MEMORY_WND = 1,
	ID_NEW_DISASM_WND,
	ID_NEW_LOG_WND,
	ID_RUN,
	ID_RUN_AND_HIDE,
	ID_RUN_VBLANK,
	ID_RUN_IRQ,
	ID_NEXT_CPU,
	ID_STEP,
	ID_STEP_OVER,
	ID_STEP_OUT,
	ID_HARD_RESET,
	ID_SOFT_RESET,
	ID_EXIT,

	ID_1_BYTE_CHUNKS,
	ID_2_BYTE_CHUNKS,
	ID_4_BYTE_CHUNKS,
	ID_8_BYTE_CHUNKS,
	ID_LOGICAL_ADDRESSES,
	ID_PHYSICAL_ADDRESSES,
	ID_REVERSE_VIEW,
	ID_INCREASE_MEM_WIDTH,
	ID_DECREASE_MEM_WIDTH,

	ID_SHOW_RAW,
	ID_SHOW_ENCRYPTED,
	ID_SHOW_COMMENTS,
	ID_RUN_TO_CURSOR,
	ID_TOGGLE_BREAKPOINT,

	ID_DEVICE_OPTIONS  // keep this always at the end
};



//============================================================
//  TYPES
//============================================================

struct debugview_info;
class debugwin_info;


struct debugview_info
{
	debugwin_info *         owner;
	debug_view *            view;
	HWND                    wnd;
	HWND                    hscroll;
	HWND                    vscroll;
};


class debugwin_info
{
public:
	debugwin_info(running_machine &machine)
		: m_machine(machine) { }

	running_machine &machine() const { return m_machine; }

	debugwin_info *         next;
	HWND                    wnd;
	HWND                    focuswnd;
	WNDPROC                 handler;

	UINT32                  minwidth, maxwidth;
	UINT32                  minheight, maxheight;
	void                    (*recompute_children)(debugwin_info *);
	void                    (*update_menu)(debugwin_info *);

	int                     (*handle_command)(debugwin_info *, WPARAM, LPARAM);
	int                     (*handle_key)(debugwin_info *, WPARAM, LPARAM);
	UINT16                  ignore_char_lparam;

	debugview_info          view[MAX_VIEWS];

	HWND                    editwnd;
	char                    edit_defstr[256];
	void                    (*process_string)(debugwin_info *, const char *);
	WNDPROC                 original_editproc;
	TCHAR                   history[HISTORY_LENGTH][MAX_EDIT_STRING];
	int                     history_count;
	int                     last_history;

	HWND                    otherwnd[MAX_OTHER_WND];

private:
	running_machine &       m_machine;
};


//============================================================
//  LOCAL VARIABLES
//============================================================

static debugwin_info *window_list;
static debugwin_info *main_console;
static UINT32 main_console_regwidth;

static UINT8 waiting_for_debugger;

static HFONT debug_font;
static UINT32 debug_font_height;
static UINT32 debug_font_width;
static UINT32 debug_font_ascent;

static UINT32 hscroll_height;
static UINT32 vscroll_width;



//============================================================
//  PROTOTYPES
//============================================================

static debugwin_info *debugwin_window_create(running_machine &machine, LPCSTR title, WNDPROC handler);
static void debugwin_window_free(debugwin_info *info);
static LRESULT CALLBACK debugwin_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);

static void debugwin_view_draw_contents(debugview_info *view, HDC dc);
static LRESULT CALLBACK debugwin_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);
static void debugwin_view_update(debug_view &view, void *osdprivate);
static int debugwin_view_create(debugwin_info *info, int which, debug_view_type type);

static LRESULT CALLBACK debugwin_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam);

//static void generic_create_window(int type);
static void generic_recompute_children(debugwin_info *info);

static void memory_create_window(running_machine &machine);
static void memory_recompute_children(debugwin_info *info);
static void memory_process_string(debugwin_info *info, const char *string);
static void memory_update_menu(debugwin_info *info);
static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam);
static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam);
static void memory_update_caption(running_machine &machine, HWND wnd);

static void disasm_create_window(running_machine &machine);
static void disasm_recompute_children(debugwin_info *info);
static void disasm_process_string(debugwin_info *info, const char *string);
static void disasm_update_menu(debugwin_info *info);
static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam);
static int disasm_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam);
static void disasm_update_caption(running_machine &machine, HWND wnd);

static void console_create_window(running_machine &machine);
static void console_recompute_children(debugwin_info *info);
static void console_process_string(debugwin_info *info, const char *string);
static void console_set_cpu(device_t *device);

static HMENU create_standard_menubar(void);
static int global_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam);
static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam);
static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds);
static void smart_show_window(HWND wnd, BOOL show);
static void smart_show_all(BOOL show);

static void image_update_menu(debugwin_info *info);


//-------------------------------------------------
//  debugger_windows - constructor
//-------------------------------------------------
debugger_windows::debugger_windows(const osd_interface &osd)
	: osd_debugger_interface(osd)
{
}

//============================================================
//  wait_for_debugger
//============================================================

void debugger_windows::wait_for_debugger(device_t &device, bool firststop)
{
	MSG message;

	// create a console window
	if (main_console == NULL)
		console_create_window(m_osd.machine());

	// update the views in the console to reflect the current CPU
	if (main_console != NULL)
		console_set_cpu(&device);

	// when we are first stopped, adjust focus to us
	if (firststop && main_console != NULL)
	{
		SetForegroundWindow(main_console->wnd);
		if (winwindow_has_focus())
			SetFocus(main_console->editwnd);
	}

	// make sure the debug windows are visible
	waiting_for_debugger = TRUE;
	smart_show_all(TRUE);

	// run input polling to ensure that our status is in sync
	wininput_poll(m_osd.machine());

	// get and process messages
	GetMessage(&message, NULL, 0, 0);

	switch (message.message)
	{
		// check for F10 -- we need to capture that ourselves
		case WM_SYSKEYDOWN:
		case WM_SYSKEYUP:
			if (message.wParam == VK_F4 && message.message == WM_SYSKEYDOWN)
				SendMessage(GetAncestor(GetFocus(), GA_ROOT), WM_CLOSE, 0, 0);
			if (message.wParam == VK_F10)
				SendMessage(GetAncestor(GetFocus(), GA_ROOT), (message.message == WM_SYSKEYDOWN) ? WM_KEYDOWN : WM_KEYUP, message.wParam, message.lParam);
			break;

		// process everything else
		default:
			winwindow_dispatch_message(m_osd.machine(), &message);
			break;
	}

	// mark the debugger as active
	waiting_for_debugger = FALSE;
}



//============================================================
//  debugwin_seq_pressed
//============================================================

static int debugwin_seq_pressed(running_machine &machine)
{
	const input_seq &seq = machine.ioport().type_seq(IPT_UI_DEBUG_BREAK);
	int result = FALSE;
	int invert = FALSE;
	int first = TRUE;
	int codenum;

	// iterate over all of the codes
	int length = seq.length();
	for (codenum = 0; codenum < length; codenum++)
	{
		input_code code = seq[codenum];

		// handle NOT
		if (code == input_seq::not_code)
			invert = TRUE;

		// handle OR and END
		else if (code == input_seq::or_code || code == input_seq::end_code)
		{
			// if we have a positive result from the previous set, we're done
			if (result || code == input_seq::end_code)
				break;

			// otherwise, reset our state
			result = FALSE;
			invert = FALSE;
			first = TRUE;
		}

		// handle everything else as a series of ANDs
		else
		{
			int vkey = wininput_vkey_for_mame_code(code);
			int pressed = (vkey != 0 && (GetAsyncKeyState(vkey) & 0x8000) != 0);

			// if this is the first in the sequence, result is set equal
			if (first)
				result = pressed ^ invert;

			// further values are ANDed
			else if (result)
				result &= pressed ^ invert;

			// no longer first, and clear the invert flag
			first = invert = FALSE;
		}
	}

	// return the result if we queried at least one switch
	return result;
}



//============================================================
//  debugwin_init_windows
//============================================================

void debugger_windows::init_debugger()
{
	static int class_registered;

	// register the window classes
	if (!class_registered)
	{
		WNDCLASS wc = { 0 };

		// initialize the description of the window class
		wc.lpszClassName    = TEXT("MAMEDebugWindow");
		wc.hInstance        = GetModuleHandle(NULL);
		wc.lpfnWndProc      = debugwin_window_proc;
		wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
		wc.hIcon            = LoadIcon(wc.hInstance, MAKEINTRESOURCE(2));
		wc.lpszMenuName     = NULL;
		wc.hbrBackground    = NULL;
		wc.style            = 0;
		wc.cbClsExtra       = 0;
		wc.cbWndExtra       = 0;

		// register the class; fail if we can't
		if (!RegisterClass(&wc))
			fatalerror("Unable to register debug window class\n");

		// initialize the description of the view class
		wc.lpszClassName    = TEXT("MAMEDebugView");
		wc.lpfnWndProc      = debugwin_view_proc;

		// register the class; fail if we can't
		if (!RegisterClass(&wc))
			fatalerror("Unable to register debug view class\n");

		class_registered = TRUE;
	}

	// create the font
	if (debug_font == NULL)
	{
		// create a temporary DC
		HDC temp_dc = GetDC(NULL);
		TEXTMETRIC metrics;
		HGDIOBJ old_font;

		if (temp_dc != NULL)
		{
			windows_options &options = downcast<windows_options &>(m_osd.machine().options());
			int size = options.debugger_font_size();
			TCHAR *t_face;

			// create a standard font
			t_face = tstring_from_utf8(options.debugger_font());
			debug_font = CreateFont(-MulDiv(size, GetDeviceCaps(temp_dc, LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE,
						ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH, t_face);
			osd_free(t_face);
			t_face = NULL;

			if (debug_font == NULL)
				fatalerror("Unable to create debugger font\n");

			// get the metrics
			old_font = SelectObject(temp_dc, debug_font);
			if (GetTextMetrics(temp_dc, &metrics))
			{
				debug_font_width = metrics.tmAveCharWidth;
				debug_font_height = metrics.tmHeight;
				debug_font_ascent = metrics.tmAscent + metrics.tmExternalLeading;
			}
			SelectObject(temp_dc, old_font);
			ReleaseDC(NULL, temp_dc);
		}
	}

	// get other metrics
	hscroll_height = GetSystemMetrics(SM_CYHSCROLL);
	vscroll_width = GetSystemMetrics(SM_CXVSCROLL);
}



//============================================================
//  debugwin_destroy_windows
//============================================================

void debugger_windows::debugger_exit()
{
	// loop over windows and free them
	while (window_list != NULL)
	{
		// clear the view list because they will be freed by the core
		memset(window_list->view, 0, sizeof(window_list->view));
		DestroyWindow(window_list->wnd);
	}

	main_console = NULL;
}



//============================================================
//  debugger_update
//============================================================

void debugger_windows::debugger_update()
{
	// if we're running live, do some checks
	if (!winwindow_has_focus() && !debug_cpu_is_stopped(m_osd.machine()) && m_osd.machine().phase() == MACHINE_PHASE_RUNNING)
	{
		// see if the interrupt key is pressed and break if it is
		if (debugwin_seq_pressed(m_osd.machine()))
		{
			HWND focuswnd = GetFocus();
			debugwin_info *info;

			debug_cpu_get_visible_cpu(m_osd.machine())->debug()->halt_on_next_instruction("User-initiated break\n");

			// if we were focused on some window's edit box, reset it to default
			for (info = window_list; info != NULL; info = info->next)
				if (focuswnd == info->editwnd)
				{
					SendMessage(focuswnd, WM_SETTEXT, (WPARAM)0, (LPARAM)info->edit_defstr);
					SendMessage(focuswnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
				}
		}
	}
}



//============================================================
//  debugwin_window_create
//============================================================

static debugwin_info *debugwin_window_create(running_machine &machine, LPCSTR title, WNDPROC handler)
{
	debugwin_info *info = NULL;
	RECT work_bounds;

	// allocate memory
	info = global_alloc_clear(debugwin_info(machine));

	// create the window
	info->handler = handler;
	info->wnd = win_create_window_ex_utf8(DEBUG_WINDOW_STYLE_EX, "MAMEDebugWindow", title, DEBUG_WINDOW_STYLE,
			0, 0, 100, 100, win_window_list->hwnd, create_standard_menubar(), GetModuleHandle(NULL), info);
	if (info->wnd == NULL)
		goto cleanup;

	// fill in some defaults
	SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0);
	info->minwidth = 200;
	info->minheight = 200;
	info->maxwidth = work_bounds.right - work_bounds.left;
	info->maxheight = work_bounds.bottom - work_bounds.top;

	// set the default handlers
	info->handle_command = global_handle_command;
	info->handle_key = global_handle_key;
	strcpy(info->edit_defstr, "");

	// hook us in
	info->next = window_list;
	window_list = info;

	return info;

cleanup:
	if (info->wnd != NULL)
		DestroyWindow(info->wnd);
	global_free(info);
	return NULL;
}



//============================================================
//  debugwin_window_free
//============================================================

static void debugwin_window_free(debugwin_info *info)
{
	debugwin_info **scanptr;
	int viewnum;

	// first unlink us from the list
	for (scanptr = &window_list; *scanptr != NULL; scanptr = &(*scanptr)->next)
		if (*scanptr == info)
		{
			*scanptr = info->next;
			break;
		}

	// free any views
	for (viewnum = 0; viewnum < ARRAY_LENGTH(info->view); viewnum++)
		if (info->view[viewnum].view != NULL)
		{
			info->machine().debug_view().free_view(*info->view[viewnum].view);
			info->view[viewnum].view = NULL;
		}

	// free our memory
	global_free(info);
}



//============================================================
//  debugwin_window_draw_contents
//============================================================

static void debugwin_window_draw_contents(debugwin_info *info, HDC dc)
{
	RECT bounds, parent;
	int curview, curwnd;

	// fill the background with light gray
	GetClientRect(info->wnd, &parent);
	FillRect(dc, &parent, (HBRUSH)GetStockObject(LTGRAY_BRUSH));

	// get the parent bounds in screen coords
	ClientToScreen(info->wnd, &((POINT *)&parent)[0]);
	ClientToScreen(info->wnd, &((POINT *)&parent)[1]);

	// draw edges around all views
	for (curview = 0; curview < MAX_VIEWS; curview++)
		if (info->view[curview].wnd)
		{
			GetWindowRect(info->view[curview].wnd, &bounds);
			bounds.top -= parent.top;
			bounds.bottom -= parent.top;
			bounds.left -= parent.left;
			bounds.right -= parent.left;
			InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH);
			DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT);
		}

	// draw edges around all children
	if (info->editwnd)
	{
		GetWindowRect(info->editwnd, &bounds);
		bounds.top -= parent.top;
		bounds.bottom -= parent.top;
		bounds.left -= parent.left;
		bounds.right -= parent.left;
		InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH);
		DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT);
	}

	for (curwnd = 0; curwnd < MAX_OTHER_WND; curwnd++)
		if (info->otherwnd[curwnd])
		{
			GetWindowRect(info->otherwnd[curwnd], &bounds);
			bounds.top -= parent.top;
			bounds.bottom -= parent.top;
			bounds.left -= parent.left;
			bounds.right -= parent.left;
			InflateRect(&bounds, EDGE_WIDTH, EDGE_WIDTH);
			DrawEdge(dc, &bounds, EDGE_SUNKEN, BF_RECT);
		}
}



//============================================================
//  debugwin_window_proc
//============================================================

static LRESULT CALLBACK debugwin_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);

	// handle a few messages
	switch (message)
	{
		// set the info pointer
		case WM_CREATE:
		{
			CREATESTRUCT *createinfo = (CREATESTRUCT *)lparam;
			info = (debugwin_info *)createinfo->lpCreateParams;
			SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams);
			if (info->handler)
				SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR)info->handler);
			break;
		}

		// paint: draw bezels as necessary
		case WM_PAINT:
		{
			PAINTSTRUCT pstruct;
			HDC dc = BeginPaint(wnd, &pstruct);
			debugwin_window_draw_contents(info, dc);
			EndPaint(wnd, &pstruct);
			break;
		}

		// keydown: handle debugger keys
		case WM_KEYDOWN:
			if ((*info->handle_key)(info, wparam, lparam))
				info->ignore_char_lparam = lparam >> 16;
			break;

		// char: ignore chars associated with keys we've handled
		case WM_CHAR:
			if (info->ignore_char_lparam == (lparam >> 16))
				info->ignore_char_lparam = 0;
			else if (waiting_for_debugger || !debugwin_seq_pressed(info->machine()))
				return DefWindowProc(wnd, message, wparam, lparam);
			break;

		// activate: set the focus
		case WM_ACTIVATE:
			if (wparam != WA_INACTIVE && info->focuswnd != NULL)
				SetFocus(info->focuswnd);
			break;

		// get min/max info: set the minimum window size
		case WM_GETMINMAXINFO:
		{
			MINMAXINFO *minmax = (MINMAXINFO *)lparam;
			if (info)
			{
				minmax->ptMinTrackSize.x = info->minwidth;
				minmax->ptMinTrackSize.y = info->minheight;
				minmax->ptMaxSize.x = minmax->ptMaxTrackSize.x = info->maxwidth;
				minmax->ptMaxSize.y = minmax->ptMaxTrackSize.y = info->maxheight;
			}
			break;
		}

		// sizing: recompute child window locations
		case WM_SIZE:
		case WM_SIZING:
			if (info->recompute_children != NULL)
				(*info->recompute_children)(info);
			InvalidateRect(wnd, NULL, FALSE);
			break;

		// mouse wheel: forward to the first view
		case WM_MOUSEWHEEL:
		{
			static int units_carryover = 0;

			UINT lines_per_click;
			if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines_per_click, 0))
				lines_per_click = 3;

			int units = GET_WHEEL_DELTA_WPARAM(wparam) + units_carryover;
			int clicks = units / WHEEL_DELTA;
			units_carryover = units % WHEEL_DELTA;

			int delta = clicks * lines_per_click;
			int viewnum = 0;
			POINT point;
			HWND child;

			// figure out which view we are hovering over
			GetCursorPos(&point);
			ScreenToClient(info->wnd, &point);
			child = ChildWindowFromPoint(info->wnd, point);
			if (child)
			{
				for (viewnum = 0; viewnum < MAX_VIEWS; viewnum++)
					if (info->view[viewnum].wnd == child)
						break;
				if (viewnum == MAX_VIEWS)
					break;
			}

			// send the appropriate message to this view's scrollbar
			if (info->view[viewnum].wnd && info->view[viewnum].vscroll)
			{
				int message_type = SB_LINEUP;
				if (delta < 0)
				{
					message_type = SB_LINEDOWN;
					delta = -delta;
				}
				while (delta > 0)
				{
					SendMessage(info->view[viewnum].wnd, WM_VSCROLL, message_type, (LPARAM)info->view[viewnum].vscroll);
					delta--;
				}
			}
			break;
		}

		// activate: set the focus
		case WM_INITMENU:
			if (info->update_menu != NULL)
				(*info->update_menu)(info);
			break;

		// command: handle a comment
		case WM_COMMAND:
			if (!(*info->handle_command)(info, wparam, lparam))
				return DefWindowProc(wnd, message, wparam, lparam);
			break;

		// close: close the window if it's not the main console
		case WM_CLOSE:
			if (main_console && main_console->wnd == wnd)
			{
				smart_show_all(FALSE);
				debug_cpu_get_visible_cpu(info->machine())->debug()->go();
			}
			else
				DestroyWindow(wnd);
			break;

		// destroy: close down the window
		case WM_NCDESTROY:
			debugwin_window_free(info);
			break;

		// everything else: defaults
		default:
			return DefWindowProc(wnd, message, wparam, lparam);
	}

	return 0;
}



//============================================================
//  debugwin_view_create
//============================================================

static int debugwin_view_create(debugwin_info *info, int which, debug_view_type type)
{
	debugview_info *view = &info->view[which];

	// set the owner
	view->owner = info;

	// create the child view
	view->wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), NULL, DEBUG_VIEW_STYLE,
			0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), view);
	if (view->wnd == NULL)
		goto cleanup;

	// create the scroll bars
	view->hscroll = CreateWindowEx(HSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, HSCROLL_STYLE,
			0, 0, 100, CW_USEDEFAULT, view->wnd, NULL, GetModuleHandle(NULL), view);
	view->vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, VSCROLL_STYLE,
			0, 0, CW_USEDEFAULT, 100, view->wnd, NULL, GetModuleHandle(NULL), view);
	if (view->hscroll == NULL || view->vscroll == NULL)
		goto cleanup;

	// create the debug view
	view->view = info->machine().debug_view().alloc_view(type, debugwin_view_update, view);
	if (view->view == NULL)
		goto cleanup;

	return 1;

cleanup:
	if (view->view)
		info->machine().debug_view().free_view(*view->view);
	if (view->hscroll)
		DestroyWindow(view->hscroll);
	if (view->vscroll)
		DestroyWindow(view->vscroll);
	if (view->wnd)
		DestroyWindow(view->wnd);
	return 0;
}



//============================================================
//  debugwin_view_set_bounds
//============================================================

static void debugwin_view_set_bounds(debugview_info *info, HWND parent, const RECT *newbounds)
{
	RECT bounds = *newbounds;

	// account for the edges and set the bounds
	if (info->wnd)
		smart_set_window_bounds(info->wnd, parent, &bounds);

	// update
	debugwin_view_update(*info->view, info);
}



//============================================================
//  debugwin_view_draw_contents
//============================================================

static void debugwin_view_draw_contents(debugview_info *view, HDC windc)
{
	const debug_view_char *viewdata = view->view->viewdata();
	debug_view_xy visarea = view->view->visible_size();
	HGDIOBJ oldfont, oldbitmap;
	COLORREF oldfgcolor;
	UINT32 col, row;
	HBITMAP bitmap;
	int oldbkmode;
	RECT client;
	HDC dc;

	// get the client rect
	GetClientRect(view->wnd, &client);

	// create a compatible DC and an offscreen bitmap
	dc = CreateCompatibleDC(windc);
	if (dc == NULL)
		return;
	bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom);
	if (bitmap == NULL)
	{
		DeleteDC(dc);
		return;
	}
	oldbitmap = SelectObject(dc, bitmap);

	// set the font
	oldfont = SelectObject(dc, debug_font);
	oldfgcolor = GetTextColor(dc);
	oldbkmode = GetBkMode(dc);
	SetBkMode(dc, TRANSPARENT);

	// iterate over rows and columns
	for (row = 0; row < visarea.y; row++)
	{
		int iter;

		// loop twice; once to fill the background and once to draw the text
		for (iter = 0; iter < 2; iter++)
		{
			COLORREF fgcolor = RGB(0x00,0x00,0x00);
			COLORREF bgcolor = RGB(0xff,0xff,0xff);
			HBRUSH bgbrush = NULL;
			int last_attrib = -1;
			TCHAR buffer[256];
			int count = 0;
			RECT bounds;

			// initialize the text bounds
			bounds.left = bounds.right = 0;
			bounds.top = row * debug_font_height;
			bounds.bottom = bounds.top + debug_font_height;

			// start with a brush on iteration #0
			if (iter == 0)
				bgbrush = CreateSolidBrush(bgcolor);

			// iterate over columns
			for (col = 0; col < visarea.x; col++)
			{
				// if the attribute changed, adjust the colors
				if (viewdata[col].attrib != last_attrib)
				{
					COLORREF oldbg = bgcolor;

					// reset to standard colors
					fgcolor = RGB(0x00,0x00,0x00);
					bgcolor = RGB(0xff,0xff,0xff);

					// pick new fg/bg colors
					if (viewdata[col].attrib & DCA_ANCILLARY) bgcolor = RGB(0xe0,0xe0,0xe0);
					if (viewdata[col].attrib & DCA_SELECTED) bgcolor = RGB(0xff,0x80,0x80);
					if (viewdata[col].attrib & DCA_CURRENT) bgcolor = RGB(0xff,0xff,0x00);
					if ((viewdata[col].attrib & DCA_SELECTED) && (viewdata[col].attrib & DCA_CURRENT)) bgcolor = RGB(0xff,0xc0,0x80);
					if (viewdata[col].attrib & DCA_CHANGED) fgcolor = RGB(0xff,0x00,0x00);
					if (viewdata[col].attrib & DCA_INVALID) fgcolor = RGB(0x00,0x00,0xff);
					if (viewdata[col].attrib & DCA_DISABLED) fgcolor = RGB((GetRValue(fgcolor) + GetRValue(bgcolor)) / 2, (GetGValue(fgcolor) + GetGValue(bgcolor)) / 2, (GetBValue(fgcolor) + GetBValue(bgcolor)) / 2);
					if (viewdata[col].attrib & DCA_COMMENT) fgcolor = RGB(0x00,0x80,0x00);

					// flush any pending drawing
					if (count > 0)
					{
						bounds.right = bounds.left + count * debug_font_width;
						if (iter == 0)
							FillRect(dc, &bounds, bgbrush);
						else
							ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL);
						bounds.left = bounds.right;
						count = 0;
					}

					// set the new colors
					if (iter == 0 && oldbg != bgcolor)
					{
						DeleteObject(bgbrush);
						bgbrush = CreateSolidBrush(bgcolor);
					}
					else if (iter == 1)
						SetTextColor(dc, fgcolor);
					last_attrib = viewdata[col].attrib;
				}

				// add this character to the buffer
				buffer[count++] = viewdata[col].byte;
			}

			// flush any remaining stuff
			if (count > 0)
			{
				bounds.right = bounds.left + count * debug_font_width;
				if (iter == 0)
					FillRect(dc, &bounds, bgbrush);
				else
					ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL);
			}

			// erase to the end of the line
			if (iter == 0)
			{
				bounds.left = bounds.right;
				bounds.right = client.right;
				FillRect(dc, &bounds, bgbrush);
				DeleteObject(bgbrush);
			}
		}

		// advance viewdata
		viewdata += visarea.x;
	}

	// erase anything beyond the bottom with white
	GetClientRect(view->wnd, &client);
	client.top = visarea.y * debug_font_height;
	FillRect(dc, &client, (HBRUSH)GetStockObject(WHITE_BRUSH));

	// reset the font
	SetBkMode(dc, oldbkmode);
	SetTextColor(dc, oldfgcolor);
	SelectObject(dc, oldfont);

	// blit the final results
	BitBlt(windc, 0, 0, client.right, client.bottom, dc, 0, 0, SRCCOPY);

	// undo the offscreen stuff
	SelectObject(dc, oldbitmap);
	DeleteObject(bitmap);
	DeleteDC(dc);
}



//============================================================
//  debugwin_view_update
//============================================================

static void debugwin_view_update(debug_view &view, void *osdprivate)
{
	debugview_info *info = (debugview_info *)osdprivate;
	RECT bounds, vscroll_bounds, hscroll_bounds;
	debug_view_xy totalsize, visiblesize, topleft;
	int show_vscroll, show_hscroll;
	SCROLLINFO scrollinfo;

	assert(info->view == &view);

	// get the view window bounds
	GetClientRect(info->wnd, &bounds);
	visiblesize.x = (bounds.right - bounds.left) / debug_font_width;
	visiblesize.y = (bounds.bottom - bounds.top) / debug_font_height;

	// get the updated total rows/cols and left row/col
	totalsize = view.total_size();
	topleft = view.visible_position();

	// determine if we need to show the scrollbars
	show_vscroll = show_hscroll = FALSE;
	if (totalsize.x > visiblesize.x && bounds.bottom >= hscroll_height)
	{
		bounds.bottom -= hscroll_height;
		visiblesize.y = (bounds.bottom - bounds.top) / debug_font_height;
		show_hscroll = TRUE;
	}
	if (totalsize.y > visiblesize.y && bounds.right >= vscroll_width)
	{
		bounds.right -= vscroll_width;
		visiblesize.x = (bounds.right - bounds.left) / debug_font_width;
		show_vscroll = TRUE;
	}
	if (!show_vscroll && totalsize.y > visiblesize.y && bounds.right >= vscroll_width)
	{
		bounds.right -= vscroll_width;
		visiblesize.x = (bounds.right - bounds.left) / debug_font_width;
		show_vscroll = TRUE;
	}

	// compute the bounds of the scrollbars
	GetClientRect(info->wnd, &vscroll_bounds);
	vscroll_bounds.left = vscroll_bounds.right - vscroll_width;
	if (show_hscroll)
		vscroll_bounds.bottom -= hscroll_height;

	GetClientRect(info->wnd, &hscroll_bounds);
	hscroll_bounds.top = hscroll_bounds.bottom - hscroll_height;
	if (show_vscroll)
		hscroll_bounds.right -= vscroll_width;

	// if we hid the scrollbars, make sure we reset the top/left corners
	if (topleft.y + visiblesize.y > totalsize.y)
		topleft.y = MAX(totalsize.y - visiblesize.y, 0);
	if (topleft.x + visiblesize.x > totalsize.x)
		topleft.x = MAX(totalsize.x - visiblesize.x, 0);

	// fill out the scroll info struct for the vertical scrollbar
	scrollinfo.cbSize = sizeof(scrollinfo);
	scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
	scrollinfo.nMin = 0;
	scrollinfo.nMax = totalsize.y - 1;
	scrollinfo.nPage = visiblesize.y;
	scrollinfo.nPos = topleft.y;
	SetScrollInfo(info->vscroll, SB_CTL, &scrollinfo, TRUE);

	// fill out the scroll info struct for the horizontal scrollbar
	scrollinfo.cbSize = sizeof(scrollinfo);
	scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
	scrollinfo.nMin = 0;
	scrollinfo.nMax = totalsize.x - 1;
	scrollinfo.nPage = visiblesize.x;
	scrollinfo.nPos = topleft.x;
	SetScrollInfo(info->hscroll, SB_CTL, &scrollinfo, TRUE);

	// update window info
	visiblesize.y++;
	visiblesize.x++;
	view.set_visible_size(visiblesize);
	view.set_visible_position(topleft);

	// invalidate the bounds
	InvalidateRect(info->wnd, NULL, FALSE);

	// adjust the bounds of the scrollbars and show/hide them
	if (info->vscroll)
	{
		if (show_vscroll)
			smart_set_window_bounds(info->vscroll, info->wnd, &vscroll_bounds);
		smart_show_window(info->vscroll, show_vscroll);
	}
	if (info->hscroll)
	{
		if (show_hscroll)
			smart_set_window_bounds(info->hscroll, info->wnd, &hscroll_bounds);
		smart_show_window(info->hscroll, show_hscroll);
	}
}



//============================================================
//  debugwin_view_process_scroll
//============================================================

static UINT32 debugwin_view_process_scroll(debugview_info *info, WORD type, HWND wnd)
{
	SCROLLINFO scrollinfo;
	INT32 maxval;
	INT32 result;

	// get the current info
	scrollinfo.cbSize = sizeof(scrollinfo);
	scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
	GetScrollInfo(wnd, SB_CTL, &scrollinfo);

	// by default we stay put
	result = scrollinfo.nPos;

	// determine the maximum value
	maxval = (scrollinfo.nMax > scrollinfo.nPage) ? (scrollinfo.nMax - scrollinfo.nPage + 1) : 0;

	// handle the message
	switch (type)
	{
		case SB_THUMBTRACK:
			result = scrollinfo.nTrackPos;
			break;

		case SB_LEFT:
			result = 0;
			break;

		case SB_RIGHT:
			result = maxval;
			break;

		case SB_LINELEFT:
			result -= 1;
			break;

		case SB_LINERIGHT:
			result += 1;
			break;

		case SB_PAGELEFT:
			result -= scrollinfo.nPage - 1;
			break;

		case SB_PAGERIGHT:
			result += scrollinfo.nPage - 1;
			break;
	}

	// generic rangecheck
	if (result < 0)
		result = 0;
	if (result > maxval)
		result = maxval;

	// set the new position
	scrollinfo.fMask = SIF_POS;
	scrollinfo.nPos = result;
	SetScrollInfo(wnd, SB_CTL, &scrollinfo, TRUE);

	return (UINT32)result;
}



//============================================================
//  debugwin_view_prev_view
//============================================================

static void debugwin_view_prev_view(debugwin_info *info, debugview_info *curview)
{
	int curindex = 1;
	int numviews;

	// count the number of views
	for (numviews = 0; numviews < MAX_VIEWS; numviews++)
		if (info->view[numviews].wnd == NULL)
			break;

	// if we have a curview, find out its index
	if (curview)
		curindex = curview - &info->view[0];

	// loop until we find someone to take focus
	while (1)
	{
		// advance to the previous index
		curindex--;
		if (curindex < -1)
			curindex = numviews - 1;

		// negative numbers mean the focuswnd
		if (curindex < 0 && info->focuswnd != NULL)
		{
			SetFocus(info->focuswnd);
			break;
		}

		// positive numbers mean a view
		else if (curindex >= 0 && info->view[curindex].wnd != NULL && info->view[curindex].view->cursor_supported())
		{
			SetFocus(info->view[curindex].wnd);
			break;
		}
	}
}



//============================================================
//  debugwin_view_next_view
//============================================================

static void debugwin_view_next_view(debugwin_info *info, debugview_info *curview)
{
	int curindex = -1;
	int numviews;

	// count the number of views
	for (numviews = 0; numviews < MAX_VIEWS; numviews++)
		if (info->view[numviews].wnd == NULL)
			break;

	// if we have a curview, find out its index
	if (curview)
		curindex = curview - &info->view[0];

	// loop until we find someone to take focus
	while (1)
	{
		// advance to the previous index
		curindex++;
		if (curindex >= numviews)
			curindex = -1;

		// negative numbers mean the focuswnd
		if (curindex < 0 && info->focuswnd != NULL)
		{
			SetFocus(info->focuswnd);
			break;
		}

		// positive numbers mean a view
		else if (curindex >= 0 && info->view[curindex].wnd != NULL && info->view[curindex].view->cursor_supported())
		{
			SetFocus(info->view[curindex].wnd);
			InvalidateRect(info->view[curindex].wnd, NULL, FALSE);
			break;
		}
	}
}



//============================================================
//  debugwin_view_proc
//============================================================

static LRESULT CALLBACK debugwin_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	debugview_info *info = (debugview_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);

	// handle a few messages
	switch (message)
	{
		// set the info pointer
		case WM_CREATE:
		{
			CREATESTRUCT *createinfo = (CREATESTRUCT *)lparam;
			SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams);
			break;
		}

		// paint: redraw the last bitmap
		case WM_PAINT:
		{
			PAINTSTRUCT pstruct;
			HDC dc = BeginPaint(wnd, &pstruct);
			debugwin_view_draw_contents(info, dc);
			EndPaint(wnd, &pstruct);
			break;
		}

		// keydown: handle debugger keys
		case WM_SYSKEYDOWN:
			if (wparam != VK_F10)
				return DefWindowProc(wnd, message, wparam, lparam);
			// (fall through)
		case WM_KEYDOWN:
		{
			if ((*info->owner->handle_key)(info->owner, wparam, lparam))
				info->owner->ignore_char_lparam = lparam >> 16;
			else
			{
				switch (wparam)
				{
					case VK_UP:
						info->view->process_char(DCH_UP);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_DOWN:
						info->view->process_char(DCH_DOWN);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_LEFT:
						if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
							info->view->process_char(DCH_CTRLLEFT);
						else
							info->view->process_char(DCH_LEFT);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_RIGHT:
						if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
							info->view->process_char(DCH_CTRLRIGHT);
						else
							info->view->process_char(DCH_RIGHT);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_PRIOR:
						info->view->process_char(DCH_PUP);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_NEXT:
						info->view->process_char(DCH_PDOWN);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_HOME:
						if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
							info->view->process_char(DCH_CTRLHOME);
						else
							info->view->process_char(DCH_HOME);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_END:
						if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
							info->view->process_char(DCH_CTRLEND);
						else
							info->view->process_char(DCH_END);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_ESCAPE:
						if (info->owner->focuswnd != NULL)
							SetFocus(info->owner->focuswnd);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;

					case VK_TAB:
						if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
							debugwin_view_prev_view(info->owner, info);
						else
							debugwin_view_next_view(info->owner, info);
						info->owner->ignore_char_lparam = lparam >> 16;
						break;
				}
			}
			break;
		}

		// char: ignore chars associated with keys we've handled
		case WM_CHAR:
		{
			if (info->owner->ignore_char_lparam == (lparam >> 16))
				info->owner->ignore_char_lparam = 0;
			else if (waiting_for_debugger || !debugwin_seq_pressed(info->owner->machine()))
			{
				if (wparam >= 32 && wparam < 127 && info->view->cursor_supported())
					info->view->process_char(wparam);
				else
					return DefWindowProc(wnd, message, wparam, lparam);
			}
			break;
		}

		// gaining focus
		case WM_SETFOCUS:
		{
			if (info->view->cursor_supported())
				info->view->set_cursor_visible(true);
			break;
		}

		// losing focus
		case WM_KILLFOCUS:
		{
			if (info->view->cursor_supported())
				info->view->set_cursor_visible(false);
			break;
		}

		// mouse click
		case WM_LBUTTONDOWN:
		{
			if (info->view->cursor_supported())
			{
				debug_view_xy topleft = info->view->visible_position();
				debug_view_xy newpos;
				newpos.x = topleft.x + GET_X_LPARAM(lparam) / debug_font_width;
				newpos.y = topleft.y + GET_Y_LPARAM(lparam) / debug_font_height;
				info->view->set_cursor_position(newpos);
				SetFocus(wnd);
			}
			break;
		}

		// hscroll
		case WM_HSCROLL:
		{
			debug_view_xy topleft = info->view->visible_position();
			topleft.x = debugwin_view_process_scroll(info, LOWORD(wparam), (HWND)lparam);
			info->view->set_visible_position(topleft);
			info->owner->machine().debug_view().flush_osd_updates();
			break;
		}

		// vscroll
		case WM_VSCROLL:
		{
			debug_view_xy topleft = info->view->visible_position();
			topleft.y = debugwin_view_process_scroll(info, LOWORD(wparam), (HWND)lparam);
			info->view->set_visible_position(topleft);
			info->owner->machine().debug_view().flush_osd_updates();
			break;
		}

		// everything else: defaults
		default:
			return DefWindowProc(wnd, message, wparam, lparam);
	}

	return 0;
}



//============================================================
//  debugwin_edit_proc
//============================================================

static LRESULT CALLBACK debugwin_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);
	TCHAR buffer[MAX_EDIT_STRING];

	// handle a few messages
	switch (message)
	{
		// key down: handle navigation in the attached view
		case WM_SYSKEYDOWN:
			if (wparam != VK_F10)
				return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam);
			// (fall through)
		case WM_KEYDOWN:
			switch (wparam)
			{
				case VK_UP:
					if (info->last_history < info->history_count - 1)
						info->last_history++;
					else
						info->last_history = 0;
					SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]);
					SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING);
					break;

				case VK_DOWN:
					if (info->last_history > 0)
						info->last_history--;
					else if (info->history_count > 0)
						info->last_history = info->history_count - 1;
					else
						info->last_history = 0;
					SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]);
					SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING);
					break;

				case VK_PRIOR:
					if (info->view[0].wnd && info->view[0].vscroll)
						SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)info->view[0].vscroll);
					break;

				case VK_NEXT:
					if (info->view[0].wnd && info->view[0].vscroll)
						SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)info->view[0].vscroll);
					break;

				case VK_TAB:
					if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
						debugwin_view_prev_view(info, NULL);
					else
						debugwin_view_next_view(info, NULL);
					info->ignore_char_lparam = lparam >> 16;
					break;

				default:
					if ((*info->handle_key)(info, wparam, lparam))
						info->ignore_char_lparam = lparam >> 16;
					else
						return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam);
					break;
			}
			break;

		// char: handle the return key
		case WM_CHAR:

			// ignore chars associated with keys we've handled
			if (info->ignore_char_lparam == (lparam >> 16))
				info->ignore_char_lparam = 0;
			else if (waiting_for_debugger || !debugwin_seq_pressed(info->machine()))
			{
				switch (wparam)
				{
					case 13:
					{
						// fetch the text
						SendMessage(wnd, WM_GETTEXT, (WPARAM)ARRAY_LENGTH(buffer), (LPARAM)buffer);

						// add to the history if it's not a repeat of the last one
						if (buffer[0] != 0 && _tcscmp(buffer, &info->history[0][0]))
						{
							memmove(&info->history[1][0], &info->history[0][0], (HISTORY_LENGTH - 1) * MAX_EDIT_STRING);
							_tcscpy(&info->history[0][0], buffer);
							if (info->history_count < HISTORY_LENGTH)
								info->history_count++;
						}
						info->last_history = info->history_count - 1;

						// process
						if (info->process_string)
						{
							char *utf8_buffer = utf8_from_tstring(buffer);
							if (utf8_buffer != NULL)
							{
								(*info->process_string)(info, utf8_buffer);
								osd_free(utf8_buffer);
							}
						}
						break;
					}

					case 27:
					{
						// fetch the text
						SendMessage(wnd, WM_GETTEXT, (WPARAM)sizeof(buffer), (LPARAM)buffer);

						// if it's not empty, clear the text
						if (_tcslen(buffer) > 0)
						{
							info->ignore_char_lparam = lparam >> 16;
							SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)info->edit_defstr);
						}
						break;
					}

					default:
						return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam);
				}
			}
			break;

		// everything else: defaults
		default:
			return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam);
	}

	return 0;
}



//============================================================
//  generic_create_window
//============================================================

#ifdef UNUSED_FUNCTION
static void generic_create_window(running_machine &machine, debug_view_type type)
{
	debugwin_info *info;
	char title[256];

	// create the window
	_snprintf(title, ARRAY_LENGTH(title), "Debug: %s [%s]", machine.system().description, machine.system().name);
	info = debugwin_window_create(machine, title, NULL);
	if (info == NULL || !debugwin_view_create(info, 0, type))
		return;

	// set the child function
	info->recompute_children = generic_recompute_children;

	// recompute the children once to get the maxwidth
	generic_recompute_children(info);

	// position the window and recompute children again
	SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW);
	generic_recompute_children(info);
}
#endif



//============================================================
//  generic_recompute_children
//============================================================

static void generic_recompute_children(debugwin_info *info)
{
	debug_view_xy totalsize = info->view[0].view->total_size();
	RECT parent;
	RECT bounds;

	// compute a client rect
	bounds.top = bounds.left = 0;
	bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
	bounds.bottom = 200;
	AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);

	// clamp the min/max size
	info->maxwidth = bounds.right - bounds.left;

	// get the parent's dimensions
	GetClientRect(info->wnd, &parent);

	// view gets the remaining space
	InflateRect(&parent, -EDGE_WIDTH, -EDGE_WIDTH);
	debugwin_view_set_bounds(&info->view[0], info->wnd, &parent);
}



//============================================================
//  log_create_window
//============================================================

static void log_create_window(running_machine &machine)
{
	debug_view_xy totalsize;
	debugwin_info *info;
	char title[256];
	RECT bounds;

	// create the window
	_snprintf(title, ARRAY_LENGTH(title), "Errorlog: %s [%s]", machine.system().description, machine.system().name);
	info = debugwin_window_create(machine, title, NULL);
	if (info == NULL || !debugwin_view_create(info, 0, DVT_LOG))
		return;

	// set the child function
	info->recompute_children = generic_recompute_children;

	// get the view width
	totalsize = info->view[0].view->total_size();

	// compute a client rect
	bounds.top = bounds.left = 0;
	bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
	bounds.bottom = 200;
	AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);

	// clamp the min/max size
	info->maxwidth = bounds.right - bounds.left;

	// position the window at the bottom-right
	SetWindowPos(info->wnd, HWND_TOP,
				100, 100,
				bounds.right - bounds.left, bounds.bottom - bounds.top,
				SWP_SHOWWINDOW);

	// recompute the children and set focus on the edit box
	generic_recompute_children(info);
}



//============================================================
//  memory_create_window
//============================================================

static void memory_create_window(running_machine &machine)
{
	device_t *curcpu = debug_cpu_get_visible_cpu(machine);
	debugwin_info *info;
	HMENU optionsmenu;

	// create the window
	info = debugwin_window_create(machine, "Memory", NULL);
	if (info == NULL || !debugwin_view_create(info, 0, DVT_MEMORY))
		return;

	// set the handlers
	info->handle_command = memory_handle_command;
	info->handle_key = memory_handle_key;
	info->update_menu = memory_update_menu;

	// create the options menu
	optionsmenu = CreatePopupMenu();
	AppendMenu(optionsmenu, MF_ENABLED, ID_1_BYTE_CHUNKS, TEXT("1-byte chunks\tCtrl+1"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_2_BYTE_CHUNKS, TEXT("2-byte chunks\tCtrl+2"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_4_BYTE_CHUNKS, TEXT("4-byte chunks\tCtrl+4"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_8_BYTE_CHUNKS, TEXT("8-byte chunks\tCtrl+8"));
	AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(optionsmenu, MF_ENABLED, ID_LOGICAL_ADDRESSES, TEXT("Logical Addresses\tCtrl+L"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_PHYSICAL_ADDRESSES, TEXT("Physical Addresses\tCtrl+Y"));
	AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(optionsmenu, MF_ENABLED, ID_REVERSE_VIEW, TEXT("Reverse View\tCtrl+R"));
	AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(optionsmenu, MF_ENABLED, ID_INCREASE_MEM_WIDTH, TEXT("Increase bytes per line\tCtrl+P"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_DECREASE_MEM_WIDTH, TEXT("Decrease bytes per line\tCtrl+O"));
	AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options"));

	// set up the view to track the initial expression
	downcast<debug_view_memory *>(info->view[0].view)->set_expression("0");
	strcpy(info->edit_defstr, "0");

	// create an edit box and override its key handling
	info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE,
			0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL);
	info->original_editproc = (WNDPROC)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC);
	SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info);
	SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debugwin_edit_proc);
	SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
	SendMessage(info->editwnd, WM_SETTEXT, (WPARAM)0, (LPARAM)TEXT("0"));
	SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0);
	SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);

	// create a combo box
	info->otherwnd[0] = CreateWindowEx(COMBO_BOX_STYLE_EX, TEXT("COMBOBOX"), NULL, COMBO_BOX_STYLE,
			0, 0, 100, 1000, info->wnd, NULL, GetModuleHandle(NULL), NULL);
	SetWindowLongPtr(info->otherwnd[0], GWLP_USERDATA, (LONG_PTR)info);
	SendMessage(info->otherwnd[0], WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);

	// populate the combobox
	int maxlength = 0;
	for (const debug_view_source *source = info->view[0].view->first_source(); source != NULL; source = source->next())
	{
		int length = strlen(source->name());
		if (length > maxlength)
			maxlength = length;
		TCHAR *t_name = tstring_from_utf8(source->name());
		SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)t_name);
		osd_free(t_name);
	}
	const debug_view_source *source = info->view[0].view->source_for_device(curcpu);
	SendMessage(info->otherwnd[0], CB_SETCURSEL, info->view[0].view->source_list().indexof(*source), 0);
	SendMessage(info->otherwnd[0], CB_SETDROPPEDWIDTH, (maxlength + 2) * debug_font_width + vscroll_width, 0);
	info->view[0].view->set_source(*source);

	// set the child functions
	info->recompute_children = memory_recompute_children;
	info->process_string = memory_process_string;

	// set the caption
	memory_update_caption(machine, info->wnd);

	// recompute the children once to get the maxwidth
	memory_recompute_children(info);

	// position the window and recompute children again
	SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW);
	memory_recompute_children(info);

	// mark the edit box as the default focus and set it
	info->focuswnd = info->editwnd;
	SetFocus(info->editwnd);
}



//============================================================
//  memory_recompute_children
//============================================================

static void memory_recompute_children(debugwin_info *info)
{
	debug_view_xy totalsize = info->view[0].view->total_size();
	RECT parent, memrect, editrect, comborect;
	RECT bounds;

	// compute a client rect
	bounds.top = bounds.left = 0;
	bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
	bounds.bottom = 200;
	AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);

	// clamp the min/max size
	info->maxwidth = bounds.right - bounds.left;

	// get the parent's dimensions
	GetClientRect(info->wnd, &parent);

	// edit box gets half of the width
	editrect.top = parent.top + EDGE_WIDTH;
	editrect.bottom = editrect.top + debug_font_height + 4;
	editrect.left = parent.left + EDGE_WIDTH;
	editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH;

	// combo box gets the other half of the width
	comborect.top = editrect.top;
	comborect.bottom = editrect.bottom;
	comborect.left = editrect.right + 2 * EDGE_WIDTH;
	comborect.right = parent.right - EDGE_WIDTH;

	// memory view gets the rest
	memrect.top = editrect.bottom + 2 * EDGE_WIDTH;
	memrect.bottom = parent.bottom - EDGE_WIDTH;
	memrect.left = parent.left + EDGE_WIDTH;
	memrect.right = parent.right - EDGE_WIDTH;

	// set the bounds of things
	debugwin_view_set_bounds(&info->view[0], info->wnd, &memrect);
	smart_set_window_bounds(info->editwnd, info->wnd, &editrect);
	smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect);
}



//============================================================
//  memory_process_string
//============================================================

static void memory_process_string(debugwin_info *info, const char *string)
{
	// set the string to the memory view
	downcast<debug_view_memory *>(info->view[0].view)->set_expression(string);

	// select everything in the edit text box
	SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);

	// update the default string to match
	strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1);
}



//============================================================
//  memory_update_menu
//============================================================

static void memory_update_menu(debugwin_info *info)
{
	debug_view_memory *memview = downcast<debug_view_memory *>(info->view[0].view);
	CheckMenuItem(GetMenu(info->wnd), ID_1_BYTE_CHUNKS, MF_BYCOMMAND | (memview->bytes_per_chunk() == 1 ? MF_CHECKED : MF_UNCHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_2_BYTE_CHUNKS, MF_BYCOMMAND | (memview->bytes_per_chunk() == 2 ? MF_CHECKED : MF_UNCHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_4_BYTE_CHUNKS, MF_BYCOMMAND | (memview->bytes_per_chunk() == 4 ? MF_CHECKED : MF_UNCHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_8_BYTE_CHUNKS, MF_BYCOMMAND | (memview->bytes_per_chunk() == 8 ? MF_CHECKED : MF_UNCHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_LOGICAL_ADDRESSES, MF_BYCOMMAND | (memview->physical() ? MF_UNCHECKED : MF_CHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_PHYSICAL_ADDRESSES, MF_BYCOMMAND | (memview->physical() ? MF_CHECKED : MF_UNCHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_REVERSE_VIEW, MF_BYCOMMAND | (memview->reverse() ? MF_CHECKED : MF_UNCHECKED));
}



//============================================================
//  memory_handle_command
//============================================================

static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam)
{
	debug_view_memory *memview = downcast<debug_view_memory *>(info->view[0].view);
	switch (HIWORD(wparam))
	{
		// combo box selection changed
		case CBN_SELCHANGE:
		{
			int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0);
			if (sel != CB_ERR)
			{
				memview->set_source(*memview->source_list().find(sel));
				memory_update_caption(info->machine(), info->wnd);

				// reset the focus
				SetFocus(info->focuswnd);
				return 1;
			}
			break;
		}

		// menu selections
		case 0:
			switch (LOWORD(wparam))
			{
				case ID_1_BYTE_CHUNKS:
					memview->set_bytes_per_chunk(1);
					return 1;

				case ID_2_BYTE_CHUNKS:
					memview->set_bytes_per_chunk(2);
					return 1;

				case ID_4_BYTE_CHUNKS:
					memview->set_bytes_per_chunk(4);
					return 1;

				case ID_8_BYTE_CHUNKS:
					memview->set_bytes_per_chunk(8);
					return 1;

				case ID_LOGICAL_ADDRESSES:
					memview->set_physical(false);
					return 1;

				case ID_PHYSICAL_ADDRESSES:
					memview->set_physical(true);
					return 1;

				case ID_REVERSE_VIEW:
					memview->set_reverse(!memview->reverse());
					return 1;

				case ID_INCREASE_MEM_WIDTH:
					memview->set_chunks_per_row(memview->chunks_per_row() + 1);
					return 1;

				case ID_DECREASE_MEM_WIDTH:
					memview->set_chunks_per_row(memview->chunks_per_row() - 1);
					return 1;
			}
			break;
	}
	return global_handle_command(info, wparam, lparam);
}



//============================================================
//  memory_handle_key
//============================================================

static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam)
{
	if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
	{
		switch (wparam)
		{
			case '1':
				SendMessage(info->wnd, WM_COMMAND, ID_1_BYTE_CHUNKS, 0);
				return 1;

			case '2':
				SendMessage(info->wnd, WM_COMMAND, ID_2_BYTE_CHUNKS, 0);
				return 1;

			case '4':
				SendMessage(info->wnd, WM_COMMAND, ID_4_BYTE_CHUNKS, 0);
				return 1;

			case '8':
				SendMessage(info->wnd, WM_COMMAND, ID_8_BYTE_CHUNKS, 0);
				return 1;

			case 'L':
				SendMessage(info->wnd, WM_COMMAND, ID_LOGICAL_ADDRESSES, 0);
				return 1;

			case 'Y':
				SendMessage(info->wnd, WM_COMMAND, ID_PHYSICAL_ADDRESSES, 0);
				return 1;

			case 'R':
				SendMessage(info->wnd, WM_COMMAND, ID_REVERSE_VIEW, 0);
				return 1;

			case 'P':
				SendMessage(info->wnd, WM_COMMAND, ID_INCREASE_MEM_WIDTH, 0);
				return 1;

			case 'O':
				SendMessage(info->wnd, WM_COMMAND, ID_DECREASE_MEM_WIDTH, 0);
				return 1;
		}
	}
	return global_handle_key(info, wparam, lparam);
}



//============================================================
//  memory_update_caption
//============================================================

static void memory_update_caption(running_machine &machine, HWND wnd)
{
	debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);
	astring title;

	title.printf("Memory: %s", info->view[0].view->source()->name());
	win_set_window_text_utf8(wnd, title);
}



//============================================================
//  disasm_create_window
//============================================================

static void disasm_create_window(running_machine &machine)
{
	device_t *curcpu = debug_cpu_get_visible_cpu(machine);
	debugwin_info *info;
	HMENU optionsmenu;

	// create the window
	info = debugwin_window_create(machine, "Disassembly", NULL);
	if (info == NULL || !debugwin_view_create(info, 0, DVT_DISASSEMBLY))
		return;

	// create the options menu
	optionsmenu = CreatePopupMenu();
	AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4"));
	AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+M"));
	AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options"));

	// set the handlers
	info->handle_command = disasm_handle_command;
	info->handle_key = disasm_handle_key;
	info->update_menu = disasm_update_menu;

	// set up the view to track the initial expression
	downcast<debug_view_disasm *>(info->view[0].view)->set_expression("curpc");
	strcpy(info->edit_defstr, "curpc");

	// create an edit box and override its key handling
	info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE,
			0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL);
	info->original_editproc = (WNDPROC)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC);
	SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info);
	SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debugwin_edit_proc);
	SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
	SendMessage(info->editwnd, WM_SETTEXT, (WPARAM)0, (LPARAM)TEXT("curpc"));
	SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0);
	SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);

	// create a combo box
	info->otherwnd[0] = CreateWindowEx(COMBO_BOX_STYLE_EX, TEXT("COMBOBOX"), NULL, COMBO_BOX_STYLE,
			0, 0, 100, 1000, info->wnd, NULL, GetModuleHandle(NULL), NULL);
	SetWindowLongPtr(info->otherwnd[0], GWLP_USERDATA, (LONG_PTR)info);
	SendMessage(info->otherwnd[0], WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);

	// populate the combobox
	int maxlength = 0;
	for (const debug_view_source *source = info->view[0].view->first_source(); source != NULL; source = source->next())
	{
		int length = strlen(source->name());
		if (length > maxlength)
			maxlength = length;
		TCHAR *t_name = tstring_from_utf8(source->name());
		SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)t_name);
		osd_free(t_name);
	}
	const debug_view_source *source = info->view[0].view->source_for_device(curcpu);
	SendMessage(info->otherwnd[0], CB_SETCURSEL, info->view[0].view->source_list().indexof(*source), 0);
	SendMessage(info->otherwnd[0], CB_SETDROPPEDWIDTH, (maxlength + 2) * debug_font_width + vscroll_width, 0);
	info->view[0].view->set_source(*source);

	// set the child functions
	info->recompute_children = disasm_recompute_children;
	info->process_string = disasm_process_string;

	// set the caption
	disasm_update_caption(machine, info->wnd);

	// recompute the children once to get the maxwidth
	disasm_recompute_children(info);

	// position the window and recompute children again
	SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW);
	disasm_recompute_children(info);

	// mark the edit box as the default focus and set it
	info->focuswnd = info->editwnd;
	SetFocus(info->editwnd);
}



//============================================================
//  disasm_recompute_children
//============================================================

static void disasm_recompute_children(debugwin_info *info)
{
	debug_view_xy totalsize = info->view[0].view->total_size();
	RECT parent, dasmrect, editrect, comborect;
	RECT bounds;

	// compute a client rect
	bounds.top = bounds.left = 0;
	bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH;
	bounds.bottom = 200;
	AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);

	// clamp the min/max size
	info->maxwidth = bounds.right - bounds.left;

	// get the parent's dimensions
	GetClientRect(info->wnd, &parent);

	// edit box gets half of the width
	editrect.top = parent.top + EDGE_WIDTH;
	editrect.bottom = editrect.top + debug_font_height + 4;
	editrect.left = parent.left + EDGE_WIDTH;
	editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH;

	// combo box gets the other half of the width
	comborect.top = editrect.top;
	comborect.bottom = editrect.bottom;
	comborect.left = editrect.right + 2 * EDGE_WIDTH;
	comborect.right = parent.right - EDGE_WIDTH;

	// disasm view gets the rest
	dasmrect.top = editrect.bottom + 2 * EDGE_WIDTH;
	dasmrect.bottom = parent.bottom - EDGE_WIDTH;
	dasmrect.left = parent.left + EDGE_WIDTH;
	dasmrect.right = parent.right - EDGE_WIDTH;

	// set the bounds of things
	debugwin_view_set_bounds(&info->view[0], info->wnd, &dasmrect);
	smart_set_window_bounds(info->editwnd, info->wnd, &editrect);
	smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect);
}



//============================================================
//  disasm_process_string
//============================================================

static void disasm_process_string(debugwin_info *info, const char *string)
{
	// set the string to the disasm view
	downcast<debug_view_disasm *>(info->view[0].view)->set_expression(string);

	// select everything in the edit text box
	SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);

	// update the default string to match
	strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1);
}



//============================================================
//  disasm_update_menu
//============================================================

static void disasm_update_menu(debugwin_info *info)
{
	disasm_right_column rightcol = downcast<debug_view_disasm *>(info->view[0].view)->right_column();
	CheckMenuItem(GetMenu(info->wnd), ID_SHOW_RAW, MF_BYCOMMAND | (rightcol == DASM_RIGHTCOL_RAW ? MF_CHECKED : MF_UNCHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_SHOW_ENCRYPTED, MF_BYCOMMAND | (rightcol == DASM_RIGHTCOL_ENCRYPTED ? MF_CHECKED : MF_UNCHECKED));
	CheckMenuItem(GetMenu(info->wnd), ID_SHOW_COMMENTS, MF_BYCOMMAND | (rightcol == DASM_RIGHTCOL_COMMENTS ? MF_CHECKED : MF_UNCHECKED));
}



//============================================================
//  disasm_handle_command
//============================================================

static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam)
{
	debug_view_disasm *dasmview = downcast<debug_view_disasm *>(info->view[0].view);
	char command[64];

	switch (HIWORD(wparam))
	{
		// combo box selection changed
		case CBN_SELCHANGE:
		{
			int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0);
			if (sel != CB_ERR)
			{
				dasmview->set_source(*dasmview->source_list().find(sel));
				disasm_update_caption(info->machine(), info->wnd);

				// reset the focus
				SetFocus(info->focuswnd);
				return 1;
			}
			break;
		}

		// menu selections
		case 0:
			switch (LOWORD(wparam))
			{
				case ID_SHOW_RAW:
					dasmview->set_right_column(DASM_RIGHTCOL_RAW);
					(*info->recompute_children)(info);
					return 1;

				case ID_SHOW_ENCRYPTED:
					dasmview->set_right_column(DASM_RIGHTCOL_ENCRYPTED);
					(*info->recompute_children)(info);
					return 1;

				case ID_SHOW_COMMENTS:
					dasmview->set_right_column(DASM_RIGHTCOL_COMMENTS);
					(*info->recompute_children)(info);
					return 1;

				case ID_RUN_TO_CURSOR:
					if (dasmview->cursor_visible() && debug_cpu_get_visible_cpu(info->machine()) == dasmview->source()->device())
					{
						offs_t address = dasmview->selected_address();
						sprintf(command, "go 0x%X", address);
						debug_console_execute_command(info->machine(), command, 1);
					}
					return 1;

				case ID_TOGGLE_BREAKPOINT:
					if (dasmview->cursor_visible() && debug_cpu_get_visible_cpu(info->machine()) == dasmview->source()->device())
					{
						offs_t address = dasmview->selected_address();
						device_debug *debug = dasmview->source()->device()->debug();
						INT32 bpindex = -1;

						/* first find an existing breakpoint at this address */
						for (device_debug::breakpoint *bp = debug->breakpoint_first(); bp != NULL; bp = bp->next())
							if (address == bp->address())
							{
								bpindex = bp->index();
								break;
							}

						/* if it doesn't exist, add a new one */
						if (bpindex == -1)
							sprintf(command, "bpset 0x%X", address);
						else
							sprintf(command, "bpclear 0x%X", bpindex);
						debug_console_execute_command(info->machine(), command, 1);
					}
					return 1;
			}
			break;
	}
	return global_handle_command(info, wparam, lparam);
}



//============================================================
//  disasm_handle_key
//============================================================

static int disasm_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam)
{
	if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
	{
		switch (wparam)
		{
			case 'R':
				SendMessage(info->wnd, WM_COMMAND, ID_SHOW_RAW, 0);
				return 1;

			case 'E':
				SendMessage(info->wnd, WM_COMMAND, ID_SHOW_ENCRYPTED, 0);
				return 1;

			case 'N':
				SendMessage(info->wnd, WM_COMMAND, ID_SHOW_COMMENTS, 0);
				return 1;
		}
	}

	switch (wparam)
	{
		/* ajg - steals the F4 from the global key handler - but ALT+F4 didn't work anyways ;) */
		case VK_F4:
			SendMessage(info->wnd, WM_COMMAND, ID_RUN_TO_CURSOR, 0);
			return 1;

		case VK_F9:
			SendMessage(info->wnd, WM_COMMAND, ID_TOGGLE_BREAKPOINT, 0);
			return 1;

		case VK_RETURN:
			if (info->view[0].view->cursor_visible())
			{
				SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0);
				return 1;
			}
			break;
	}

	return global_handle_key(info, wparam, lparam);
}



//============================================================
//  disasm_update_caption
//============================================================

static void disasm_update_caption(running_machine &machine, HWND wnd)
{
	debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA);
	astring title;

	title.printf("Disassembly: %s", info->view[0].view->source()->name());
	win_set_window_text_utf8(wnd, title);
}


enum
{
	DEVOPTION_OPEN,
	DEVOPTION_CREATE,
	DEVOPTION_CLOSE,
	DEVOPTION_CASSETTE_STOPPAUSE,
	DEVOPTION_CASSETTE_PLAY,
	DEVOPTION_CASSETTE_RECORD,
	DEVOPTION_CASSETTE_REWIND,
	DEVOPTION_CASSETTE_FASTFORWARD,
	DEVOPTION_MAX
};

//============================================================
//  memory_update_menu
//============================================================

static void image_update_menu(debugwin_info *info)
{
	UINT32 cnt = 0;
	HMENU devicesmenu;

	DeleteMenu(GetMenu(info->wnd), 2, MF_BYPOSITION);

	// create the image menu
	devicesmenu = CreatePopupMenu();
	image_interface_iterator iter(info->machine().root_device());
	for (device_image_interface *img = iter.first(); img != NULL; img = iter.next())
	{
		astring temp;
		UINT flags_for_exists;
		UINT flags_for_writing;
		HMENU devicesubmenu = CreatePopupMenu();

		UINT_PTR new_item = ID_DEVICE_OPTIONS + (cnt * DEVOPTION_MAX);

		flags_for_exists = MF_ENABLED | MF_STRING;

		if (!img->exists())
			flags_for_exists |= MF_GRAYED;

		flags_for_writing = flags_for_exists;
		if (img->is_readonly())
			flags_for_writing |= MF_GRAYED;

		AppendMenu(devicesubmenu, MF_STRING, new_item + DEVOPTION_OPEN, TEXT("Mount..."));

		/*if (img->is_creatable())
		    AppendMenu(devicesubmenu, MF_STRING, new_item + DEVOPTION_CREATE, TEXT("Create..."));
		*/
		AppendMenu(devicesubmenu, flags_for_exists, new_item + DEVOPTION_CLOSE, TEXT("Unmount"));

		if (img->device().type() == CASSETTE)
		{
			cassette_state state;
			state = (cassette_state)(img->exists() ? (dynamic_cast<cassette_image_device*>(&img->device())->get_state() & CASSETTE_MASK_UISTATE) : CASSETTE_STOPPED);
			AppendMenu(devicesubmenu, MF_SEPARATOR, 0, NULL);
			AppendMenu(devicesubmenu, flags_for_exists  | ((state == CASSETTE_STOPPED)  ? MF_CHECKED : 0), new_item + DEVOPTION_CASSETTE_STOPPAUSE, TEXT("Pause/Stop"));
			AppendMenu(devicesubmenu, flags_for_exists  | ((state == CASSETTE_PLAY) ? MF_CHECKED : 0), new_item + DEVOPTION_CASSETTE_PLAY, TEXT("Play"));
			AppendMenu(devicesubmenu, flags_for_writing | ((state == CASSETTE_RECORD)   ? MF_CHECKED : 0), new_item + DEVOPTION_CASSETTE_RECORD, TEXT("Record"));
			AppendMenu(devicesubmenu, flags_for_exists, new_item + DEVOPTION_CASSETTE_REWIND, TEXT("Rewind"));
			AppendMenu(devicesubmenu, flags_for_exists, new_item + DEVOPTION_CASSETTE_FASTFORWARD, TEXT("Fast Forward"));
		}

		temp.format("%s :%s", img->device().name(), img->exists() ? img->filename() : "[empty slot]");

		AppendMenu(devicesmenu, MF_ENABLED | MF_POPUP, (UINT_PTR)devicesubmenu,  tstring_from_utf8(temp.cstr()));

		cnt++;
	}
	AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)devicesmenu, TEXT("Images"));

}

//============================================================
//  console_create_window
//============================================================

void console_create_window(running_machine &machine)
{
	debugwin_info *info;
	int bestwidth, bestheight;
	RECT bounds, work_bounds;
	HMENU optionsmenu;
	UINT32 conchars;

	// create the window
	info = debugwin_window_create(machine, "Debug", NULL);
	if (info == NULL)
		return;
	main_console = info;

	// create the views
	if (!debugwin_view_create(info, 0, DVT_DISASSEMBLY))
		goto cleanup;
	if (!debugwin_view_create(info, 1, DVT_STATE))
		goto cleanup;
	if (!debugwin_view_create(info, 2, DVT_CONSOLE))
		goto cleanup;

	// create the options menu
	optionsmenu = CreatePopupMenu();
	AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4"));
	AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E"));
	AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+N"));
	AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options"));

	// Add image menu only if image devices exist
	{
		image_interface_iterator iter(machine.root_device());
		if (iter.first() != NULL) {
			info->update_menu = image_update_menu;
			image_update_menu(info);
		}
	}

	// set the handlers
	info->handle_command = disasm_handle_command;
	info->handle_key = disasm_handle_key;

	// create an edit box and override its key handling
	info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE,
			0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL);
	info->original_editproc = (WNDPROC)(FPTR)GetWindowLongPtr(info->editwnd, GWLP_WNDPROC);
	SetWindowLongPtr(info->editwnd, GWLP_USERDATA, (LONG_PTR)info);
	SetWindowLongPtr(info->editwnd, GWLP_WNDPROC, (LONG_PTR)debugwin_edit_proc);
	SendMessage(info->editwnd, WM_SETFONT, (WPARAM)debug_font, (LPARAM)FALSE);
	SendMessage(info->editwnd, EM_LIMITTEXT, (WPARAM)MAX_EDIT_STRING, (LPARAM)0);

	// set the child functions
	info->recompute_children = console_recompute_children;
	info->process_string = console_process_string;

	// loop over all register views and get the maximum size
	main_console_regwidth = 0;
	for (const debug_view_source *source = info->view[1].view->first_source(); source != NULL; source = source->next())
	{
		UINT32 regchars;

		// set the view and fetch the width
		info->view[1].view->set_source(*source);
		regchars = info->view[1].view->total_size().x;

		// track the maximum
		main_console_regwidth = MAX(regchars, main_console_regwidth);
	}

	// determine the width of the console (this is fixed)
	conchars = info->view[2].view->total_size().x;

	// loop over all CPUs and compute the width range based on dasm width
	info->minwidth = 0;
	info->maxwidth = 0;
	for (const debug_view_source *source = info->view[0].view->first_source(); source != NULL; source = source->next())
	{
		UINT32 minwidth, maxwidth, dischars;

		// set the view and fetch the width
		info->view[0].view->set_source(*source);
		dischars = info->view[0].view->total_size().x;

		// compute the preferred width
		minwidth = EDGE_WIDTH + main_console_regwidth * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + 100 + EDGE_WIDTH;
		maxwidth = EDGE_WIDTH + main_console_regwidth * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + MAX(dischars, conchars) * debug_font_width + vscroll_width + EDGE_WIDTH;
		info->minwidth = MAX(info->minwidth, minwidth);
		info->maxwidth = MAX(info->maxwidth, maxwidth);
	}

	// get the work bounds
	SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0);

	// adjust the min/max sizes for the window style
	bounds.top = bounds.left = 0;
	bounds.right = bounds.bottom = info->minwidth;
	AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
	info->minwidth = bounds.right - bounds.left;

	bounds.top = bounds.left = 0;
	bounds.right = bounds.bottom = info->maxwidth;
	AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX);
	info->maxwidth = bounds.right - bounds.left;

	// position the window at the bottom-right
	bestwidth = (info->maxwidth < (work_bounds.right - work_bounds.left)) ? info->maxwidth : (work_bounds.right - work_bounds.left);
	bestheight = (500 < (work_bounds.bottom - work_bounds.top)) ? 500 : (work_bounds.bottom - work_bounds.top);
	SetWindowPos(info->wnd, HWND_TOP,
				work_bounds.right - bestwidth, work_bounds.bottom - bestheight,
				bestwidth, bestheight,
				SWP_SHOWWINDOW);

	// recompute the children
	console_set_cpu(debug_cpu_get_visible_cpu(machine));

	// set up the disassembly view to track the current pc
	downcast<debug_view_disasm *>(info->view[0].view)->set_expression("curpc");

	// mark the edit box as the default focus and set it
	info->focuswnd = info->editwnd;
	SetFocus(info->editwnd);
	return;

cleanup:
	if (info->view[2].view)
		machine.debug_view().free_view(*info->view[2].view);
	if (info->view[1].view)
		machine.debug_view().free_view(*info->view[1].view);
	if (info->view[0].view)
		machine.debug_view().free_view(*info->view[0].view);
}



//============================================================
//  console_recompute_children
//============================================================

static void console_recompute_children(debugwin_info *info)
{
	RECT parent, regrect, disrect, conrect, editrect;
	UINT32 regchars;//, dischars, conchars;

	// get the parent's dimensions
	GetClientRect(info->wnd, &parent);

	// get the total width of all three children
	//dischars = info->view[0].view->total_size().x;
	regchars = main_console_regwidth;
	//conchars = info->view[2].view->total_size().x;

	// registers always get their desired width, and span the entire height
	regrect.top = parent.top + EDGE_WIDTH;
	regrect.bottom = parent.bottom - EDGE_WIDTH;
	regrect.left = parent.left + EDGE_WIDTH;
	regrect.right = regrect.left + debug_font_width * regchars + vscroll_width;

	// edit box goes at the bottom of the remaining area
	editrect.bottom = parent.bottom - EDGE_WIDTH;
	editrect.top = editrect.bottom - debug_font_height - 4;
	editrect.left = regrect.right + EDGE_WIDTH * 2;
	editrect.right = parent.right - EDGE_WIDTH;

	// console and disassembly split the difference
	disrect.top = parent.top + EDGE_WIDTH;
	disrect.bottom = (editrect.top - parent.top) / 2 - EDGE_WIDTH;
	disrect.left = regrect.right + EDGE_WIDTH * 2;
	disrect.right = parent.right - EDGE_WIDTH;

	conrect.top = disrect.bottom + EDGE_WIDTH * 2;
	conrect.bottom = editrect.top - EDGE_WIDTH;
	conrect.left = regrect.right + EDGE_WIDTH * 2;
	conrect.right = parent.right - EDGE_WIDTH;

	// set the bounds of things
	debugwin_view_set_bounds(&info->view[0], info->wnd, &disrect);
	debugwin_view_set_bounds(&info->view[1], info->wnd, &regrect);
	debugwin_view_set_bounds(&info->view[2], info->wnd, &conrect);
	smart_set_window_bounds(info->editwnd, info->wnd, &editrect);
}



//============================================================
//  console_process_string
//============================================================

static void console_process_string(debugwin_info *info, const char *string)
{
	TCHAR buffer = 0;

	// an empty string is a single step
	if (string[0] == 0)
		debug_cpu_get_visible_cpu(info->machine())->debug()->single_step();

	// otherwise, just process the command
	else
		debug_console_execute_command(info->machine(), string, 1);

	// clear the edit text box
	SendMessage(info->editwnd, WM_SETTEXT, 0, (LPARAM)&buffer);
}



//============================================================
//  console_set_cpu
//============================================================

static void console_set_cpu(device_t *device)
{
	// first set all the views to the new cpu number
	main_console->view[0].view->set_source(*main_console->view[0].view->source_for_device(device));
	main_console->view[1].view->set_source(*main_console->view[1].view->source_for_device(device));

	// then update the caption
	char curtitle[256];
	astring title;

	title.printf("Debug: %s - %s '%s'", device->machine().system().name, device->name(), device->tag());
	win_get_window_text_utf8(main_console->wnd, curtitle, ARRAY_LENGTH(curtitle));
	if (title.cmp(curtitle) != 0)
		win_set_window_text_utf8(main_console->wnd, title);

	// and recompute the children
	console_recompute_children(main_console);
}



//============================================================
//  create_standard_menubar
//============================================================

static HMENU create_standard_menubar(void)
{
	HMENU menubar, debugmenu;

	// create the debug menu
	debugmenu = CreatePopupMenu();
	if (debugmenu == NULL)
		return NULL;
	AppendMenu(debugmenu, MF_ENABLED, ID_NEW_MEMORY_WND, TEXT("New Memory Window\tCtrl+M"));
	AppendMenu(debugmenu, MF_ENABLED, ID_NEW_DISASM_WND, TEXT("New Disassembly Window\tCtrl+D"));
	AppendMenu(debugmenu, MF_ENABLED, ID_NEW_LOG_WND, TEXT("New Error Log Window\tCtrl+L"));
	AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(debugmenu, MF_ENABLED, ID_RUN, TEXT("Run\tF5"));
	AppendMenu(debugmenu, MF_ENABLED, ID_RUN_AND_HIDE, TEXT("Run and Hide Debugger\tF12"));
	AppendMenu(debugmenu, MF_ENABLED, ID_NEXT_CPU, TEXT("Run to Next CPU\tF6"));
	AppendMenu(debugmenu, MF_ENABLED, ID_RUN_IRQ, TEXT("Run until Next Interrupt on This CPU\tF7"));
	AppendMenu(debugmenu, MF_ENABLED, ID_RUN_VBLANK, TEXT("Run until Next VBLANK\tF8"));
	AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(debugmenu, MF_ENABLED, ID_STEP, TEXT("Step Into\tF11"));
	AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OVER, TEXT("Step Over\tF10"));
	AppendMenu(debugmenu, MF_ENABLED, ID_STEP_OUT, TEXT("Step Out\tShift+F11"));
	AppendMenu(debugmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT(""));
	AppendMenu(debugmenu, MF_ENABLED, ID_SOFT_RESET, TEXT("Soft Reset\tF3"));
	AppendMenu(debugmenu, MF_ENABLED, ID_HARD_RESET, TEXT("Hard Reset\tShift+F3"));
	AppendMenu(debugmenu, MF_ENABLED, ID_EXIT, TEXT("Exit"));

	// create the menu bar
	menubar = CreateMenu();
	if (menubar == NULL)
	{
		DestroyMenu(debugmenu);
		return NULL;
	}
	AppendMenu(menubar, MF_ENABLED | MF_POPUP, (UINT_PTR)debugmenu, TEXT("Debug"));

	return menubar;
}

//============================================================
//  copy_extension_list
//============================================================

static int copy_extension_list(char *dest, size_t dest_len, const char *extensions)
{
	const char *s;
	int pos = 0;

	// our extension lists are comma delimited; Win32 expects to see lists
	// delimited by semicolons
	s = extensions;
	while(*s)
	{
		// append a semicolon if not at the beginning
		if (s != extensions)
			pos += snprintf(&dest[pos], dest_len - pos, ";");

		// append ".*"
		pos += snprintf(&dest[pos], dest_len - pos, "*.");

		// append the file extension
		while(*s && (*s != ','))
		{
			pos += snprintf(&dest[pos], dest_len - pos, "%c", *s);
			s++;
		}

		// if we found a comma, advance
		while(*s == ',')
			s++;
	}
	return pos;
}

//============================================================
//  add_filter_entry
//============================================================

static int add_filter_entry(char *dest, size_t dest_len, const char *description, const char *extensions)
{
	int pos = 0;

	// add the description
	pos += snprintf(&dest[pos], dest_len - pos, "%s (", description);

	// add the extensions to the description
	pos += copy_extension_list(&dest[pos], dest_len - pos, extensions);

	// add the trailing rparen and '|' character
	pos += snprintf(&dest[pos], dest_len - pos, ")|");

	// now add the extension list itself
	pos += copy_extension_list(&dest[pos], dest_len - pos, extensions);

	// append a '|'
	pos += snprintf(&dest[pos], dest_len - pos, "|");

	return pos;
}
//============================================================
//  build_generic_filter
//============================================================

static void build_generic_filter(device_image_interface *img, int is_save, char *filter, size_t filter_len)
{
	char *s;

	const char *file_extension;

	/* copy the string */
	file_extension = img->file_extensions();

	// start writing the filter
	s = filter;

	// common image types
	s += add_filter_entry(filter, filter_len, "Common image types", file_extension);

	// all files
	s += sprintf(s, "All files (*.*)|*.*|");

	// compressed
	if (!is_save)
		s += sprintf(s, "Compressed Images (*.zip)|*.zip|");

	*(s++) = '\0';
}

//============================================================
//  global_handle_command
//============================================================

static int global_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam)
{
	if (HIWORD(wparam) == 0)
		switch (LOWORD(wparam))
		{
			case ID_NEW_MEMORY_WND:
				memory_create_window(info->machine());
				return 1;

			case ID_NEW_DISASM_WND:
				disasm_create_window(info->machine());
				return 1;

			case ID_NEW_LOG_WND:
				log_create_window(info->machine());
				return 1;

			case ID_RUN_AND_HIDE:
				smart_show_all(FALSE);
			case ID_RUN:
				debug_cpu_get_visible_cpu(info->machine())->debug()->go();
				return 1;

			case ID_NEXT_CPU:
				debug_cpu_get_visible_cpu(info->machine())->debug()->go_next_device();
				return 1;

			case ID_RUN_VBLANK:
				debug_cpu_get_visible_cpu(info->machine())->debug()->go_vblank();
				return 1;

			case ID_RUN_IRQ:
				debug_cpu_get_visible_cpu(info->machine())->debug()->go_interrupt();
				return 1;

			case ID_STEP:
				debug_cpu_get_visible_cpu(info->machine())->debug()->single_step();
				return 1;

			case ID_STEP_OVER:
				debug_cpu_get_visible_cpu(info->machine())->debug()->single_step_over();
				return 1;

			case ID_STEP_OUT:
				debug_cpu_get_visible_cpu(info->machine())->debug()->single_step_out();
				return 1;

			case ID_HARD_RESET:
				info->machine().schedule_hard_reset();
				return 1;

			case ID_SOFT_RESET:
				info->machine().schedule_soft_reset();
				debug_cpu_get_visible_cpu(info->machine())->debug()->go();
				return 1;

			case ID_EXIT:
				if (info->focuswnd != NULL)
					SetFocus(info->focuswnd);
				info->machine().schedule_exit();
				return 1;
		}
		if (LOWORD(wparam) >= ID_DEVICE_OPTIONS) {
			UINT32 devid = (LOWORD(wparam) - ID_DEVICE_OPTIONS) / DEVOPTION_MAX;
			image_interface_iterator iter(info->machine().root_device());
			device_image_interface *img = iter.byindex(devid);
			if (img!=NULL) {
				switch ((LOWORD(wparam) - ID_DEVICE_OPTIONS) % DEVOPTION_MAX)
				{
					case DEVOPTION_OPEN :
											{
												char filter[2048];
												build_generic_filter(img, FALSE, filter, ARRAY_LENGTH(filter));

												TCHAR selectedFilename[MAX_PATH];
												selectedFilename[0] = '\0';
												LPTSTR buffer;
												LPTSTR t_filter = NULL;

												buffer = tstring_from_utf8(filter);

												// convert a pipe-char delimited string into a NUL delimited string
												t_filter = (LPTSTR) alloca((_tcslen(buffer) + 2) * sizeof(*t_filter));
												int i = 0;
												for (i = 0; buffer[i] != '\0'; i++)
													t_filter[i] = (buffer[i] != '|') ? buffer[i] : '\0';
												t_filter[i++] = '\0';
												t_filter[i++] = '\0';
												osd_free(buffer);


												OPENFILENAME  ofn;
												memset(&ofn,0,sizeof(ofn));
												ofn.lStructSize = sizeof(ofn);
												ofn.hwndOwner = NULL;
												ofn.lpstrFile = selectedFilename;
												ofn.lpstrFile[0] = '\0';
												ofn.nMaxFile = MAX_PATH;
												ofn.lpstrFilter = t_filter;
												ofn.nFilterIndex = 1;
												ofn.lpstrFileTitle = NULL;
												ofn.nMaxFileTitle = 0;
												ofn.lpstrInitialDir = NULL;
												ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

												if (GetOpenFileName(&ofn)) {
													img->load(utf8_from_tstring(selectedFilename));
												}
											}
											return 1;
/*                  case DEVOPTION_CREATE:
                                            return 1;*/
					case DEVOPTION_CLOSE:
											img->unload();
											return 1;
					default:
						if (img->device().type() == CASSETTE) {
							cassette_image_device* cassette = dynamic_cast<cassette_image_device*>(&img->device());
							switch ((LOWORD(wparam) - ID_DEVICE_OPTIONS) % DEVOPTION_MAX)
							{
								case DEVOPTION_CASSETTE_STOPPAUSE:
														cassette->change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
														return 1;
								case DEVOPTION_CASSETTE_PLAY:
														cassette->change_state(CASSETTE_PLAY, CASSETTE_MASK_UISTATE);
														return 1;
								case DEVOPTION_CASSETTE_RECORD:
														cassette->change_state(CASSETTE_RECORD, CASSETTE_MASK_UISTATE);
														return 1;
								case DEVOPTION_CASSETTE_REWIND:
														cassette->seek(-60.0, SEEK_CUR);
														return 1;
								case DEVOPTION_CASSETTE_FASTFORWARD:
														cassette->seek(+60.0, SEEK_CUR);
														return 1;
							}
						}
				}
			}
		}
	return 0;
}



//============================================================
//  global_handle_key
//============================================================

static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam)
{
	/* ignore any keys that are received while the debug key is down */
	if (!waiting_for_debugger && debugwin_seq_pressed(info->machine()))
		return 1;

	switch (wparam)
	{
		case VK_F3:
			if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
				SendMessage(info->wnd, WM_COMMAND, ID_HARD_RESET, 0);
			else
				SendMessage(info->wnd, WM_COMMAND, ID_SOFT_RESET, 0);
			return 1;

		case VK_F4:
			if (GetAsyncKeyState(VK_MENU) & 0x8000)
			{
				/* ajg - never gets here since 'alt' seems to be captured somewhere else - menu maybe? */
				SendMessage(info->wnd, WM_COMMAND, ID_EXIT, 0);
				return 1;
			}
			break;

		case VK_F5:
			SendMessage(info->wnd, WM_COMMAND, ID_RUN, 0);
			return 1;

		case VK_F6:
			SendMessage(info->wnd, WM_COMMAND, ID_NEXT_CPU, 0);
			return 1;

		case VK_F7:
			SendMessage(info->wnd, WM_COMMAND, ID_RUN_IRQ, 0);
			return 1;

		case VK_F8:
			SendMessage(info->wnd, WM_COMMAND, ID_RUN_VBLANK, 0);
			return 1;

		case VK_F10:
			SendMessage(info->wnd, WM_COMMAND, ID_STEP_OVER, 0);
			return 1;

		case VK_F11:
			if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
				SendMessage(info->wnd, WM_COMMAND, ID_STEP_OUT, 0);
			else
				SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0);
			return 1;

		case VK_F12:
			SendMessage(info->wnd, WM_COMMAND, ID_RUN_AND_HIDE, 0);
			return 1;

		case 'M':
			if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
			{
				SendMessage(info->wnd, WM_COMMAND, ID_NEW_MEMORY_WND, 0);
				return 1;
			}
			break;

		case 'D':
			if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
			{
				SendMessage(info->wnd, WM_COMMAND, ID_NEW_DISASM_WND, 0);
				return 1;
			}
			break;

		case 'L':
			if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
			{
				SendMessage(info->wnd, WM_COMMAND, ID_NEW_LOG_WND, 0);
				return 1;
			}
			break;
	}

	return 0;
}



//============================================================
//  smart_set_window_bounds
//============================================================

static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds)
{
	RECT curbounds;
	int flags = 0;

	// first get the current bounds, relative to the parent
	GetWindowRect(wnd, &curbounds);
	if (parent != NULL)
	{
		RECT parentbounds;
		GetWindowRect(parent, &parentbounds);
		curbounds.top -= parentbounds.top;
		curbounds.bottom -= parentbounds.top;
		curbounds.left -= parentbounds.left;
		curbounds.right -= parentbounds.left;
	}

	// if the position matches, don't change it
	if (curbounds.top == bounds->top && curbounds.left == bounds->left)
		flags |= SWP_NOMOVE;
	if ((curbounds.bottom - curbounds.top) == (bounds->bottom - bounds->top) &&
		(curbounds.right - curbounds.left) == (bounds->right - bounds->left))
		flags |= SWP_NOSIZE;

	// if we need to, reposition the window
	if (flags != (SWP_NOMOVE | SWP_NOSIZE))
		SetWindowPos(wnd, NULL,
					bounds->left, bounds->top,
					bounds->right - bounds->left, bounds->bottom - bounds->top,
					SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | flags);
}



//============================================================
//  smart_show_window
//============================================================

static void smart_show_window(HWND wnd, BOOL show)
{
	BOOL visible = IsWindowVisible(wnd);
	if ((visible && !show) || (!visible && show))
		ShowWindow(wnd, show ? SW_SHOW : SW_HIDE);
}



//============================================================
//  smart_show_all
//============================================================

static void smart_show_all(BOOL show)
{
	debugwin_info *info;
	if (!show)
		SetForegroundWindow(win_window_list->hwnd);
	for (info = window_list; info != NULL; info = info->next)
		smart_show_window(info->wnd, show);
}
