mirror of
https://github.com/holub/mame
synced 2025-05-06 22:35:43 +03:00
2875 lines
84 KiB
C
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, ®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_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);
|
|
}
|