mame/src/osd/windows/debugwin.c
2011-04-19 06:02:01 +00:00

2875 lines
84 KiB
C

//============================================================
//
// debugwin.c - Win32 debug window handling
//
//============================================================
//
// Copyright Aaron Giles
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the
// following conditions are met:
//
// * Redistributions of source code must retain the above
// copyright notice, this list of conditions and the
// following disclaimer.
// * Redistributions in binary form must reproduce the
// above copyright notice, this list of conditions and
// the following disclaimer in the documentation and/or
// other materials provided with the distribution.
// * Neither the name 'MAME' nor the names of its
// contributors may be used to endorse or promote
// products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGE (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//============================================================
// standard windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
#ifdef _MSC_VER
#include <zmouse.h>
#endif
// MAME headers
#include "emu.h"
#include "uiinput.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 "debugwin.h"
#include "winmain.h"
#include "window.h"
#include "video.h"
#include "input.h"
#include "config.h"
#include "strconv.h"
#include "winutf8.h"
//============================================================
// 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
};
//============================================================
// TYPES
//============================================================
typedef struct _debugview_info 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);
//============================================================
// wait_for_debugger
//============================================================
void windows_osd_interface::wait_for_debugger(device_t &device, bool firststop)
{
MSG message;
// create a console window
if (main_console == NULL)
console_create_window(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(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(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 = input_type_seq(machine, IPT_UI_DEBUG_BREAK, 0, SEQ_TYPE_STANDARD);
int result = FALSE;
int invert = FALSE;
int first = TRUE;
int codenum;
// iterate over all of the codes
for (codenum = 0; codenum < ARRAY_LENGTH(seq->code); codenum++)
{
input_code code = seq->code[codenum];
// handle NOT
if (code == SEQCODE_NOT)
invert = TRUE;
// handle OR and END
else if (code == SEQCODE_OR || code == SEQCODE_END)
{
// if we have a positive result from the previous set, we're done
if (result || code == SEQCODE_END)
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 debugwin_init_windows(running_machine &machine)
{
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(NULL, IDI_APPLICATION);
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");
// 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");
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 &>(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, FF_DONTCARE, t_face);
osd_free(t_face);
// fall back to Lucida Console 8
if (debug_font == NULL)
{
debug_font = CreateFont(-MulDiv(8, GetDeviceCaps(temp_dc, LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, TEXT("Lucida Console"));
if (debug_font == NULL)
fatalerror("Unable to create debug font");
}
// 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 debugwin_destroy_windows(void)
{
// 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;
}
//============================================================
// debugwin_show
//============================================================
void debugwin_show(int type)
{
debugwin_info *info;
// loop over windows and show/hide them
for (info = window_list; info != NULL; info = info->next)
ShowWindow(info->wnd, type);
}
//============================================================
// debugwin_update_during_game
//============================================================
void debugwin_update_during_game(running_machine &machine)
{
// if we're running live, do some checks
if (!winwindow_has_focus() && !debug_cpu_is_stopped(machine) && machine.phase() == MACHINE_PHASE_RUNNING)
{
// see if the interrupt key is pressed and break if it is
if (debugwin_seq_pressed(machine))
{
HWND focuswnd = GetFocus();
debugwin_info *info;
debug_cpu_get_visible_cpu(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->source_list().head(); 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_list().match_device(curcpu);
SendMessage(info->otherwnd[0], CB_SETCURSEL, info->view[0].view->source_list().index(*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().by_index(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->source_list().head(); 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_list().match_device(curcpu);
SendMessage(info->otherwnd[0], CB_SETCURSEL, info->view[0].view->source_list().index(*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().by_index(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);
}
//============================================================
// 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"));
// 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->source_list().head(); 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->source_list().head(); 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_list().match_device(device));
main_console->view[1].view->set_source(*main_console->view[1].view->source_list().match_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;
}
//============================================================
// 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;
}
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);
}