mirror of
https://github.com/holub/mame
synced 2025-05-17 19:24:59 +03:00

Added the concept of 'subviews' to the debugger views. The core now creates a list of possible subviews, and the OSD can select between them. This removes code from the OSD that was previously required to find all possible memory and disassembly views. Added machine parameters to debugger console output functions. Fixed some oddities in the memory view. Moved globals to hang off of the machine structure. Fixed bug where the visiblecpu was not updated immediately upon a break.
2812 lines
82 KiB
C
2812 lines
82 KiB
C
//============================================================
|
|
//
|
|
// debugwin.c - Win32 debug window handling
|
|
//
|
|
// Copyright Nicola Salmoria and the MAME Team.
|
|
// Visit http://mamedev.org for licensing and usage restrictions.
|
|
//
|
|
//============================================================
|
|
|
|
// 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 "driver.h"
|
|
#include "uiinput.h"
|
|
#include "debug/debugvw.h"
|
|
#include "debug/debugcon.h"
|
|
#include "debug/debugcpu.h"
|
|
#include "debugger.h"
|
|
|
|
// MAMEOS headers
|
|
#include "debugwin.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
|
|
#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;
|
|
typedef struct _debugwin_info debugwin_info;
|
|
|
|
|
|
struct _debugview_info
|
|
{
|
|
debugwin_info * owner;
|
|
debug_view * view;
|
|
HWND wnd;
|
|
HWND hscroll;
|
|
HWND vscroll;
|
|
};
|
|
|
|
|
|
struct _debugwin_info
|
|
{
|
|
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];
|
|
|
|
running_machine * machine;
|
|
};
|
|
|
|
|
|
//============================================================
|
|
// GLOBAL VARIABLES
|
|
//============================================================
|
|
|
|
|
|
|
|
//============================================================
|
|
// LOCAL VARIABLES
|
|
//============================================================
|
|
|
|
static debugwin_info *window_list;
|
|
static debugwin_info *main_console;
|
|
|
|
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;
|
|
|
|
static DWORD last_debugger_update;
|
|
|
|
|
|
//============================================================
|
|
// 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, int 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(const device_config *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);
|
|
|
|
|
|
|
|
//============================================================
|
|
// osd_wait_for_debugger
|
|
//============================================================
|
|
|
|
void osd_wait_for_debugger(const device_config *device, int firststop)
|
|
{
|
|
MSG message;
|
|
|
|
// create a console window
|
|
if (main_console == NULL)
|
|
console_create_window(device->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(device->machine);
|
|
|
|
// get and process messages
|
|
GetMessage(&message, NULL, 0, 0);
|
|
last_debugger_update = GetTickCount();
|
|
|
|
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(device->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(void)
|
|
{
|
|
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)
|
|
{
|
|
// create a standard Lucida Console 8 font
|
|
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.tmMaxCharWidth;
|
|
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)
|
|
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; 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) && mame_get_phase(machine) == MAME_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_halt_on_next_instruction(debug_cpu_get_visible_cpu(machine), "User-initiated break\n");
|
|
|
|
// if we were focused on some window's edit box, reset it to default
|
|
for (info = window_list; info; 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 = malloc_or_die(sizeof(*info));
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
// 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, "");
|
|
|
|
info->machine = machine;
|
|
|
|
// hook us in
|
|
info->next = window_list;
|
|
window_list = info;
|
|
|
|
return info;
|
|
|
|
cleanup:
|
|
if (info->wnd != NULL)
|
|
DestroyWindow(info->wnd);
|
|
free(info);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
//============================================================
|
|
// debugwin_window_free
|
|
//============================================================
|
|
|
|
static void debugwin_window_free(debugwin_info *info)
|
|
{
|
|
debugwin_info *prev, *curr;
|
|
int viewnum;
|
|
|
|
// first unlink us from the list
|
|
for (curr = window_list, prev = NULL; curr; prev = curr, curr = curr->next)
|
|
if (curr == info)
|
|
{
|
|
if (prev)
|
|
prev->next = curr->next;
|
|
else
|
|
window_list = curr->next;
|
|
break;
|
|
}
|
|
|
|
// free any views
|
|
for (viewnum = 0; viewnum < MAX_VIEWS; viewnum++)
|
|
if (info->view[viewnum].view)
|
|
{
|
|
debug_view_free(info->view[viewnum].view);
|
|
info->view[viewnum].view = NULL;
|
|
}
|
|
|
|
// free our memory
|
|
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, 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_SIZING:
|
|
if (info->recompute_children)
|
|
(*info->recompute_children)(info);
|
|
InvalidateRect(wnd, NULL, FALSE);
|
|
break;
|
|
|
|
// mouse wheel: forward to the first view
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
int delta = (INT16)HIWORD(wparam) / WHEEL_DELTA;
|
|
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 = SB_LINELEFT;
|
|
if (delta < 0)
|
|
{
|
|
message = SB_LINERIGHT;
|
|
delta = -delta;
|
|
}
|
|
while (delta > 0)
|
|
{
|
|
SendMessage(info->view[viewnum].wnd, WM_VSCROLL, message, (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_go(info->machine, ~0);
|
|
}
|
|
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, int 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 = debug_view_alloc(info->machine, type, debugwin_view_update, view);
|
|
if (view->view == NULL)
|
|
goto cleanup;
|
|
|
|
return 1;
|
|
|
|
cleanup:
|
|
if (view->view)
|
|
debug_view_free(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 = debug_view_get_chars(view->view);
|
|
debug_view_xy visarea = debug_view_get_visible_size(view->view);
|
|
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 = 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 = debug_view_get_total_size(view);
|
|
topleft = debug_view_get_visible_position(view);
|
|
|
|
// 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++;
|
|
debug_view_set_visible_size(view, visiblesize);
|
|
debug_view_set_visible_position(view, 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 && debug_view_get_cursor_supported(info->view[curindex].view))
|
|
{
|
|
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 && debug_view_get_cursor_supported(info->view[curindex].view))
|
|
{
|
|
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_KEYDOWN:
|
|
{
|
|
if ((*info->owner->handle_key)(info->owner, wparam, lparam))
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
else
|
|
{
|
|
switch (wparam)
|
|
{
|
|
case VK_UP:
|
|
debug_view_type_character(info->view, DCH_UP);
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
break;
|
|
|
|
case VK_DOWN:
|
|
debug_view_type_character(info->view, DCH_DOWN);
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
break;
|
|
|
|
case VK_LEFT:
|
|
if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
|
|
debug_view_type_character(info->view, DCH_CTRLLEFT);
|
|
else
|
|
debug_view_type_character(info->view, DCH_LEFT);
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
break;
|
|
|
|
case VK_RIGHT:
|
|
if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
|
|
debug_view_type_character(info->view, DCH_CTRLRIGHT);
|
|
else
|
|
debug_view_type_character(info->view, DCH_RIGHT);
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
break;
|
|
|
|
case VK_PRIOR:
|
|
debug_view_type_character(info->view, DCH_PUP);
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
break;
|
|
|
|
case VK_NEXT:
|
|
debug_view_type_character(info->view, DCH_PDOWN);
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
break;
|
|
|
|
case VK_HOME:
|
|
if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
|
|
debug_view_type_character(info->view, DCH_CTRLHOME);
|
|
else
|
|
debug_view_type_character(info->view, DCH_HOME);
|
|
info->owner->ignore_char_lparam = lparam >> 16;
|
|
break;
|
|
|
|
case VK_END:
|
|
if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
|
|
debug_view_type_character(info->view, DCH_CTRLEND);
|
|
else
|
|
debug_view_type_character(info->view, 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 && debug_view_get_cursor_supported(info->view))
|
|
debug_view_type_character(info->view, wparam);
|
|
else
|
|
return DefWindowProc(wnd, message, wparam, lparam);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// gaining focus
|
|
case WM_SETFOCUS:
|
|
{
|
|
if (debug_view_get_cursor_supported(info->view))
|
|
debug_view_set_cursor_visible(info->view, TRUE);
|
|
break;
|
|
}
|
|
|
|
// losing focus
|
|
case WM_KILLFOCUS:
|
|
{
|
|
if (debug_view_get_cursor_supported(info->view))
|
|
debug_view_set_cursor_visible(info->view, FALSE);
|
|
break;
|
|
}
|
|
|
|
// mouse click
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
if (debug_view_get_cursor_supported(info->view))
|
|
{
|
|
debug_view_xy newpos;
|
|
newpos.x = GET_X_LPARAM(lparam) / debug_font_width;
|
|
newpos.y = GET_Y_LPARAM(lparam) / debug_font_height;
|
|
debug_view_set_cursor_position(info->view, newpos);
|
|
SetFocus(wnd);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// hscroll
|
|
case WM_HSCROLL:
|
|
{
|
|
debug_view_xy topleft = debug_view_get_visible_position(info->view);
|
|
topleft.x = debugwin_view_process_scroll(info, LOWORD(wparam), (HWND)lparam);
|
|
debug_view_set_visible_position(info->view, topleft);
|
|
break;
|
|
}
|
|
|
|
// vscroll
|
|
case WM_VSCROLL:
|
|
{
|
|
debug_view_xy topleft = debug_view_get_visible_position(info->view);
|
|
topleft.y = debugwin_view_process_scroll(info, LOWORD(wparam), (HWND)lparam);
|
|
debug_view_set_visible_position(info->view, topleft);
|
|
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_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);
|
|
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, int type)
|
|
{
|
|
debugwin_info *info;
|
|
char title[256];
|
|
|
|
// create the window
|
|
_snprintf(title, ARRAY_LENGTH(title), "Debug: %s [%s]", Machine->gamedrv->description, Machine->gamedrv->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 = debug_view_get_total_size(info->view[0].view);
|
|
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->gamedrv->description, machine->gamedrv->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 = debug_view_get_total_size(info->view[0].view);
|
|
|
|
// 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)
|
|
{
|
|
const device_config *curcpu = debug_cpu_get_visible_cpu(machine);
|
|
const memory_subview_item *subview;
|
|
debugwin_info *info;
|
|
HMENU optionsmenu;
|
|
int cursel = 0;
|
|
|
|
// 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
|
|
memory_view_set_expression(info->view[0].view, "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 = (void *)(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
|
|
for (subview = memory_view_get_subview_list(info->view[0].view); subview != NULL; subview = subview->next)
|
|
{
|
|
TCHAR *t_name = tstring_from_utf8(subview->name);
|
|
int item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)t_name);
|
|
free(t_name);
|
|
if (cursel == 0 && subview->space != NULL && subview->space->cpu == curcpu)
|
|
cursel = item;
|
|
}
|
|
SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0);
|
|
memory_view_set_subview(info->view[0].view, cursel);
|
|
|
|
// 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 = debug_view_get_total_size(info->view[0].view);
|
|
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
|
|
memory_view_set_expression(info->view[0].view, 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)
|
|
{
|
|
CheckMenuItem(GetMenu(info->wnd), ID_1_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 1 ? MF_CHECKED : MF_UNCHECKED));
|
|
CheckMenuItem(GetMenu(info->wnd), ID_2_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 2 ? MF_CHECKED : MF_UNCHECKED));
|
|
CheckMenuItem(GetMenu(info->wnd), ID_4_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 4 ? MF_CHECKED : MF_UNCHECKED));
|
|
CheckMenuItem(GetMenu(info->wnd), ID_8_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 8 ? MF_CHECKED : MF_UNCHECKED));
|
|
CheckMenuItem(GetMenu(info->wnd), ID_LOGICAL_ADDRESSES, MF_BYCOMMAND | (memory_view_get_physical(info->view[0].view) ? MF_UNCHECKED : MF_CHECKED));
|
|
CheckMenuItem(GetMenu(info->wnd), ID_PHYSICAL_ADDRESSES, MF_BYCOMMAND | (memory_view_get_physical(info->view[0].view) ? MF_CHECKED : MF_UNCHECKED));
|
|
CheckMenuItem(GetMenu(info->wnd), ID_REVERSE_VIEW, MF_BYCOMMAND | (memory_view_get_reverse(info->view[0].view) ? MF_CHECKED : MF_UNCHECKED));
|
|
}
|
|
|
|
|
|
|
|
//============================================================
|
|
// memory_handle_command
|
|
//============================================================
|
|
|
|
static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam)
|
|
{
|
|
switch (HIWORD(wparam))
|
|
{
|
|
// combo box selection changed
|
|
case CBN_SELCHANGE:
|
|
{
|
|
int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0);
|
|
if (sel != CB_ERR)
|
|
{
|
|
memory_view_set_subview(info->view[0].view, 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:
|
|
memory_view_set_bytes_per_chunk(info->view[0].view, 1);
|
|
return 1;
|
|
|
|
case ID_2_BYTE_CHUNKS:
|
|
memory_view_set_bytes_per_chunk(info->view[0].view, 2);
|
|
return 1;
|
|
|
|
case ID_4_BYTE_CHUNKS:
|
|
memory_view_set_bytes_per_chunk(info->view[0].view, 4);
|
|
return 1;
|
|
|
|
case ID_8_BYTE_CHUNKS:
|
|
memory_view_set_bytes_per_chunk(info->view[0].view, 8);
|
|
return 1;
|
|
|
|
case ID_LOGICAL_ADDRESSES:
|
|
memory_view_set_physical(info->view[0].view, FALSE);
|
|
return 1;
|
|
|
|
case ID_PHYSICAL_ADDRESSES:
|
|
memory_view_set_physical(info->view[0].view, TRUE);
|
|
return 1;
|
|
|
|
case ID_REVERSE_VIEW:
|
|
memory_view_set_reverse(info->view[0].view, !memory_view_get_reverse(info->view[0].view));
|
|
return 1;
|
|
|
|
case ID_INCREASE_MEM_WIDTH:
|
|
memory_view_set_chunks_per_row(info->view[0].view, memory_view_get_chunks_per_row(info->view[0].view) + 1);
|
|
return 1;
|
|
|
|
case ID_DECREASE_MEM_WIDTH:
|
|
memory_view_set_chunks_per_row(info->view[0].view, memory_view_get_chunks_per_row(info->view[0].view) - 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);
|
|
const memory_subview_item *subview = memory_view_get_current_subview(info->view[0].view);
|
|
char title[256];
|
|
|
|
sprintf(title, "Memory: %s", subview->name);
|
|
win_set_window_text_utf8(wnd, title);
|
|
}
|
|
|
|
|
|
|
|
//============================================================
|
|
// disasm_create_window
|
|
//============================================================
|
|
|
|
static void disasm_create_window(running_machine *machine)
|
|
{
|
|
const device_config *curcpu = debug_cpu_get_visible_cpu(machine);
|
|
const disasm_subview_item *subview;
|
|
debugwin_info *info;
|
|
HMENU optionsmenu;
|
|
int cursel = 0;
|
|
|
|
// 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
|
|
disasm_view_set_expression(info->view[0].view, "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 = (void *)(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
|
|
for (subview = disasm_view_get_subview_list(info->view[0].view); subview != NULL; subview = subview->next)
|
|
{
|
|
TCHAR *t_name = tstring_from_utf8(subview->name);
|
|
int item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)t_name);
|
|
free(t_name);
|
|
if (cursel == 0 && subview->space->cpu == curcpu)
|
|
cursel = item;
|
|
}
|
|
SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0);
|
|
disasm_view_set_subview(info->view[0].view, cursel);
|
|
|
|
// 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 = debug_view_get_total_size(info->view[0].view);
|
|
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
|
|
disasm_view_set_expression(info->view[0].view, 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 = disasm_view_get_right_column(info->view[0].view);
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
disasm_view_set_subview(info->view[0].view, 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:
|
|
disasm_view_set_right_column(info->view[0].view, DASM_RIGHTCOL_RAW);
|
|
(*info->recompute_children)(info);
|
|
return 1;
|
|
|
|
case ID_SHOW_ENCRYPTED:
|
|
disasm_view_set_right_column(info->view[0].view, DASM_RIGHTCOL_ENCRYPTED);
|
|
(*info->recompute_children)(info);
|
|
return 1;
|
|
|
|
case ID_SHOW_COMMENTS:
|
|
disasm_view_set_right_column(info->view[0].view, DASM_RIGHTCOL_COMMENTS);
|
|
(*info->recompute_children)(info);
|
|
return 1;
|
|
|
|
case ID_RUN_TO_CURSOR:
|
|
if (debug_view_get_cursor_visible(info->view[0].view))
|
|
{
|
|
const address_space *space = disasm_view_get_current_subview(info->view[0].view)->space;
|
|
if (debug_cpu_get_visible_cpu(info->machine) == space->cpu)
|
|
{
|
|
offs_t address = memory_byte_to_address(space, disasm_view_get_selected_address(info->view[0].view));
|
|
sprintf(command, "go %X", address);
|
|
debug_console_execute_command(info->machine, command, 1);
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
case ID_TOGGLE_BREAKPOINT:
|
|
if (debug_view_get_cursor_visible(info->view[0].view))
|
|
{
|
|
const address_space *space = disasm_view_get_current_subview(info->view[0].view)->space;
|
|
if (debug_cpu_get_visible_cpu(info->machine) == space->cpu)
|
|
{
|
|
offs_t address = memory_byte_to_address(space, disasm_view_get_selected_address(info->view[0].view));
|
|
cpu_debug_data *cpuinfo = cpu_get_debug_data(space->cpu);
|
|
debug_cpu_breakpoint *bp;
|
|
INT32 bpindex = -1;
|
|
|
|
/* first find an existing breakpoint at this address */
|
|
for (bp = cpuinfo->bplist; 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 %X", address);
|
|
else
|
|
sprintf(command, "bpclear %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 (debug_view_get_cursor_visible(info->view[0].view))
|
|
{
|
|
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);
|
|
const disasm_subview_item *subview = disasm_view_get_current_subview(info->view[0].view);
|
|
char title[256];
|
|
|
|
sprintf(title, "Disassembly: %s", subview->name);
|
|
win_set_window_text_utf8(wnd, title);
|
|
}
|
|
|
|
|
|
|
|
//============================================================
|
|
// console_create_window
|
|
//============================================================
|
|
|
|
void console_create_window(running_machine *machine)
|
|
{
|
|
const registers_subview_item *regsubview;
|
|
const disasm_subview_item *dasmsubview;
|
|
debugwin_info *info;
|
|
int bestwidth, bestheight;
|
|
RECT bounds, work_bounds;
|
|
HMENU optionsmenu;
|
|
|
|
// create the window
|
|
info = debugwin_window_create(machine, "Debug", NULL);
|
|
if (info == NULL)
|
|
return;
|
|
main_console = info;
|
|
console_set_cpu(machine->cpu[0]);
|
|
|
|
// create the views
|
|
if (!debugwin_view_create(info, 0, DVT_DISASSEMBLY))
|
|
goto cleanup;
|
|
if (!debugwin_view_create(info, 1, DVT_REGISTERS))
|
|
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;
|
|
|
|
// set up the disassembly view to track the current pc
|
|
disasm_view_set_expression(info->view[0].view, "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 = (void *)(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 CPUs and compute the sizes
|
|
info->minwidth = 0;
|
|
info->maxwidth = 0;
|
|
for (dasmsubview = disasm_view_get_subview_list(info->view[0].view); dasmsubview != NULL; dasmsubview = dasmsubview->next)
|
|
for (regsubview = registers_view_get_subview_list(info->view[1].view); regsubview != NULL; regsubview = regsubview->next)
|
|
if (dasmsubview->space->cpu == regsubview->device)
|
|
{
|
|
UINT32 regchars, dischars, conchars;
|
|
UINT32 minwidth, maxwidth;
|
|
|
|
// point all views to the appropriate index
|
|
disasm_view_set_subview(info->view[0].view, dasmsubview->index);
|
|
registers_view_set_subview(info->view[1].view, regsubview->index);
|
|
|
|
// get the total width of all three children
|
|
dischars = debug_view_get_total_size(info->view[0].view).x;
|
|
regchars = debug_view_get_total_size(info->view[1].view).x;
|
|
conchars = debug_view_get_total_size(info->view[2].view).x;
|
|
|
|
// compute the preferred width
|
|
minwidth = EDGE_WIDTH + regchars * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + 100 + EDGE_WIDTH;
|
|
maxwidth = EDGE_WIDTH + regchars * debug_font_width + vscroll_width + 2 * EDGE_WIDTH + ((dischars > conchars) ? dischars : conchars) * debug_font_width + vscroll_width + EDGE_WIDTH;
|
|
if (minwidth > info->minwidth)
|
|
info->minwidth = minwidth;
|
|
if (maxwidth > info->maxwidth)
|
|
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_recompute_children(info);
|
|
|
|
// 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)
|
|
debug_view_free(info->view[2].view);
|
|
if (info->view[1].view)
|
|
debug_view_free(info->view[1].view);
|
|
if (info->view[0].view)
|
|
debug_view_free(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 = debug_view_get_total_size(info->view[0].view).x;
|
|
regchars = debug_view_get_total_size(info->view[1].view).x;
|
|
conchars = debug_view_get_total_size(info->view[2].view).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, ®rect);
|
|
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_single_step(info->machine, 1);
|
|
|
|
// 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(const device_config *device)
|
|
{
|
|
const registers_subview_item *regsubitem;
|
|
const disasm_subview_item *dasmsubitem;
|
|
char title[256], curtitle[256];
|
|
|
|
// first set all the views to the new cpu number
|
|
if (main_console->view[0].view != NULL)
|
|
for (dasmsubitem = disasm_view_get_subview_list(main_console->view[0].view); dasmsubitem != NULL; dasmsubitem = dasmsubitem->next)
|
|
if (dasmsubitem->space->cpu == device)
|
|
{
|
|
disasm_view_set_subview(main_console->view[0].view, dasmsubitem->index);
|
|
break;
|
|
}
|
|
if (main_console->view[1].view != NULL)
|
|
for (regsubitem = registers_view_get_subview_list(main_console->view[1].view); regsubitem != NULL; regsubitem = regsubitem->next)
|
|
if (regsubitem->device == device)
|
|
{
|
|
registers_view_set_subview(main_console->view[1].view, regsubitem->index);
|
|
break;
|
|
}
|
|
|
|
// then update the caption
|
|
snprintf(title, ARRAY_LENGTH(title), "Debug: %s - %s", device->machine->gamedrv->name, regsubitem->name);
|
|
win_get_window_text_utf8(main_console->wnd, curtitle, ARRAY_LENGTH(curtitle));
|
|
if (strcmp(title, curtitle) != 0)
|
|
win_set_window_text_utf8(main_console->wnd, title);
|
|
}
|
|
|
|
|
|
|
|
//============================================================
|
|
// 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_go(info->machine, ~0);
|
|
return 1;
|
|
|
|
case ID_NEXT_CPU:
|
|
debug_cpu_next_cpu(info->machine);
|
|
return 1;
|
|
|
|
case ID_RUN_VBLANK:
|
|
debug_cpu_go_vblank(info->machine);
|
|
return 1;
|
|
|
|
case ID_RUN_IRQ:
|
|
debug_cpu_go_interrupt(info->machine, -1);
|
|
return 1;
|
|
|
|
case ID_STEP:
|
|
debug_cpu_single_step(info->machine, 1);
|
|
return 1;
|
|
|
|
case ID_STEP_OVER:
|
|
debug_cpu_single_step_over(info->machine, 1);
|
|
return 1;
|
|
|
|
case ID_STEP_OUT:
|
|
debug_cpu_single_step_out(info->machine);
|
|
return 1;
|
|
|
|
case ID_HARD_RESET:
|
|
mame_schedule_hard_reset(info->machine);
|
|
return 1;
|
|
|
|
case ID_SOFT_RESET:
|
|
mame_schedule_soft_reset(info->machine);
|
|
debug_cpu_go(info->machine, ~0);
|
|
return 1;
|
|
|
|
case ID_EXIT:
|
|
mame_schedule_exit(info->machine);
|
|
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; info = info->next)
|
|
smart_show_window(info->wnd, show);
|
|
}
|