//============================================================ // // 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 #include #include #ifdef _MSC_VER #include #endif // MAME headers #include "driver.h" #include "uiinput.h" #include "debug/debugvw.h" #include "debug/debugcon.h" #include "debug/debugcpu.h" #include "debugger.h" // MAMEOS headers #include "debugwin.h" #include "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 #define COMBO_BOX_STYLE_EX 0 // horizontal scroll bar styles #define HSCROLL_STYLE WS_CHILD | WS_VISIBLE | SBS_HORZ #define HSCROLL_STYLE_EX 0 // vertical scroll bar styles #define VSCROLL_STYLE WS_CHILD | WS_VISIBLE | SBS_VERT #define VSCROLL_STYLE_EX 0 enum { ID_NEW_MEMORY_WND = 1, ID_NEW_DISASM_WND, ID_NEW_LOG_WND, ID_RUN, ID_RUN_AND_HIDE, ID_RUN_VBLANK, ID_RUN_IRQ, ID_NEXT_CPU, ID_STEP, ID_STEP_OVER, ID_STEP_OUT, ID_HARD_RESET, ID_SOFT_RESET, ID_EXIT, ID_1_BYTE_CHUNKS, ID_2_BYTE_CHUNKS, ID_4_BYTE_CHUNKS, ID_8_BYTE_CHUNKS, ID_LOGICAL_ADDRESSES, ID_PHYSICAL_ADDRESSES, ID_REVERSE_VIEW, ID_INCREASE_MEM_WIDTH, ID_DECREASE_MEM_WIDTH, ID_SHOW_RAW, ID_SHOW_ENCRYPTED, ID_SHOW_COMMENTS, ID_RUN_TO_CURSOR, ID_TOGGLE_BREAKPOINT }; //============================================================ // TYPES //============================================================ typedef struct _debugview_info debugview_info; typedef struct _debugwin_info debugwin_info; struct _debugview_info { debugwin_info * owner; debug_view * view; HWND wnd; HWND hscroll; HWND vscroll; }; struct _debugwin_info { debugwin_info * next; HWND wnd; HWND focuswnd; WNDPROC handler; UINT32 minwidth, maxwidth; UINT32 minheight, maxheight; void (*recompute_children)(debugwin_info *); void (*update_menu)(debugwin_info *); int (*handle_command)(debugwin_info *, WPARAM, LPARAM); int (*handle_key)(debugwin_info *, WPARAM, LPARAM); UINT16 ignore_char_lparam; debugview_info view[MAX_VIEWS]; HWND editwnd; char edit_defstr[256]; void (*process_string)(debugwin_info *, const char *); WNDPROC original_editproc; TCHAR history[HISTORY_LENGTH][MAX_EDIT_STRING]; int history_count; int last_history; HWND otherwnd[MAX_OTHER_WND]; running_machine * machine; }; //============================================================ // 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, int type); static LRESULT CALLBACK debugwin_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam); //static void generic_create_window(int type); static void generic_recompute_children(debugwin_info *info); static void memory_create_window(running_machine *machine); static void memory_recompute_children(debugwin_info *info); static void memory_process_string(debugwin_info *info, const char *string); static void memory_update_menu(debugwin_info *info); static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam); static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam); static void memory_update_caption(running_machine *machine, HWND wnd); static void disasm_create_window(running_machine *machine); static void disasm_recompute_children(debugwin_info *info); static void disasm_process_string(debugwin_info *info, const char *string); static void disasm_update_menu(debugwin_info *info); static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam); static int disasm_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam); static void disasm_update_caption(running_machine *machine, HWND wnd); static void console_create_window(running_machine *machine); static void console_recompute_children(debugwin_info *info); static void console_process_string(debugwin_info *info, const char *string); static void console_set_cpu(const device_config *device); static HMENU create_standard_menubar(void); static int global_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam); static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam); static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds); static void smart_show_window(HWND wnd, BOOL show); static void smart_show_all(BOOL show); //============================================================ // osd_wait_for_debugger //============================================================ void osd_wait_for_debugger(const device_config *device, int firststop) { MSG message; // create a console window if (main_console == NULL) console_create_window(device->machine); // update the views in the console to reflect the current CPU if (main_console != NULL) console_set_cpu(device); // when we are first stopped, adjust focus to us if (firststop && main_console != NULL) { SetForegroundWindow(main_console->wnd); if (winwindow_has_focus()) SetFocus(main_console->editwnd); } // make sure the debug windows are visible waiting_for_debugger = TRUE; smart_show_all(TRUE); // run input polling to ensure that our status is in sync wininput_poll(device->machine); // get and process messages GetMessage(&message, NULL, 0, 0); switch (message.message) { // check for F10 -- we need to capture that ourselves case WM_SYSKEYDOWN: case WM_SYSKEYUP: if (message.wParam == VK_F4 && message.message == WM_SYSKEYDOWN) SendMessage(GetAncestor(GetFocus(), GA_ROOT), WM_CLOSE, 0, 0); if (message.wParam == VK_F10) SendMessage(GetAncestor(GetFocus(), GA_ROOT), (message.message == WM_SYSKEYDOWN) ? WM_KEYDOWN : WM_KEYUP, message.wParam, message.lParam); break; // process everything else default: winwindow_dispatch_message(device->machine, &message); break; } // mark the debugger as active waiting_for_debugger = FALSE; } //============================================================ // debugwin_seq_pressed //============================================================ static int debugwin_seq_pressed(running_machine *machine) { const input_seq *seq = input_type_seq(machine, IPT_UI_DEBUG_BREAK, 0, SEQ_TYPE_STANDARD); int result = FALSE; int invert = FALSE; int first = TRUE; int codenum; // iterate over all of the codes for (codenum = 0; codenum < ARRAY_LENGTH(seq->code); codenum++) { input_code code = seq->code[codenum]; // handle NOT if (code == SEQCODE_NOT) invert = TRUE; // handle OR and END else if (code == SEQCODE_OR || code == SEQCODE_END) { // if we have a positive result from the previous set, we're done if (result || code == SEQCODE_END) break; // otherwise, reset our state result = FALSE; invert = FALSE; first = TRUE; } // handle everything else as a series of ANDs else { int vkey = wininput_vkey_for_mame_code(code); int pressed = (vkey != 0 && (GetAsyncKeyState(vkey) & 0x8000) != 0); // if this is the first in the sequence, result is set equal if (first) result = pressed ^ invert; // further values are ANDed else if (result) result &= pressed ^ invert; // no longer first, and clear the invert flag first = invert = FALSE; } } // return the result if we queried at least one switch return result; } //============================================================ // debugwin_init_windows //============================================================ void debugwin_init_windows(void) { static int class_registered; // register the window classes if (!class_registered) { WNDCLASS wc = { 0 }; // initialize the description of the window class wc.lpszClassName = TEXT("MAMEDebugWindow"); wc.hInstance = GetModuleHandle(NULL); wc.lpfnWndProc = debugwin_window_proc; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.lpszMenuName = NULL; wc.hbrBackground = NULL; wc.style = 0; wc.cbClsExtra = 0; wc.cbWndExtra = 0; // register the class; fail if we can't if (!RegisterClass(&wc)) fatalerror("Unable to register debug window class"); // initialize the description of the view class wc.lpszClassName = TEXT("MAMEDebugView"); wc.lpfnWndProc = debugwin_view_proc; // register the class; fail if we can't if (!RegisterClass(&wc)) fatalerror("Unable to register debug view class"); class_registered = TRUE; } // create the font if (debug_font == NULL) { // create a temporary DC HDC temp_dc = GetDC(NULL); TEXTMETRIC metrics; HGDIOBJ old_font; if (temp_dc != NULL) { int size = options_get_int(mame_options(), WINOPTION_DEBUGGER_FONT_SIZE); TCHAR *t_face; // create a standard font t_face = tstring_from_utf8(options_get_string(mame_options(), WINOPTION_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); 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) && mame_get_phase(machine) == MAME_PHASE_RUNNING) { // see if the interrupt key is pressed and break if it is if (debugwin_seq_pressed(machine)) { HWND focuswnd = GetFocus(); debugwin_info *info; debug_cpu_halt_on_next_instruction(debug_cpu_get_visible_cpu(machine), "User-initiated break\n"); // if we were focused on some window's edit box, reset it to default for (info = window_list; info != 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 = alloc_clear_or_die(debugwin_info); // create the window info->handler = handler; info->wnd = win_create_window_ex_utf8(DEBUG_WINDOW_STYLE_EX, "MAMEDebugWindow", title, DEBUG_WINDOW_STYLE, 0, 0, 100, 100, win_window_list->hwnd, create_standard_menubar(), GetModuleHandle(NULL), info); if (info->wnd == NULL) goto cleanup; // fill in some defaults SystemParametersInfo(SPI_GETWORKAREA, 0, &work_bounds, 0); info->minwidth = 200; info->minheight = 200; info->maxwidth = work_bounds.right - work_bounds.left; info->maxheight = work_bounds.bottom - work_bounds.top; // set the default handlers info->handle_command = global_handle_command; info->handle_key = global_handle_key; strcpy(info->edit_defstr, ""); info->machine = machine; // hook us in info->next = window_list; window_list = info; return info; cleanup: if (info->wnd != NULL) DestroyWindow(info->wnd); free(info); return NULL; } //============================================================ // debugwin_window_free //============================================================ static void debugwin_window_free(debugwin_info *info) { debugwin_info **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) { debug_view_free(info->view[viewnum].view); info->view[viewnum].view = NULL; } // free our memory free(info); } //============================================================ // debugwin_window_draw_contents //============================================================ static void debugwin_window_draw_contents(debugwin_info *info, HDC dc) { RECT bounds, parent; int curview, curwnd; // fill the background with light gray GetClientRect(info->wnd, &parent); FillRect(dc, &parent, (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: { int delta = (INT16)HIWORD(wparam) / WHEEL_DELTA; int viewnum = 0; POINT point; HWND child; // figure out which view we are hovering over GetCursorPos(&point); ScreenToClient(info->wnd, &point); child = ChildWindowFromPoint(info->wnd, point); if (child) { for (viewnum = 0; viewnum < MAX_VIEWS; viewnum++) if (info->view[viewnum].wnd == child) break; if (viewnum == MAX_VIEWS) break; } // send the appropriate message to this view's scrollbar if (info->view[viewnum].wnd && info->view[viewnum].vscroll) { int message = SB_LINELEFT; if (delta < 0) { message = SB_LINERIGHT; delta = -delta; } while (delta > 0) { SendMessage(info->view[viewnum].wnd, WM_VSCROLL, message, (LPARAM)info->view[viewnum].vscroll); delta--; } } break; } // activate: set the focus case WM_INITMENU: if (info->update_menu != NULL) (*info->update_menu)(info); break; // command: handle a comment case WM_COMMAND: if (!(*info->handle_command)(info, wparam, lparam)) return DefWindowProc(wnd, message, wparam, lparam); break; // close: close the window if it's not the main console case WM_CLOSE: if (main_console && main_console->wnd == wnd) { smart_show_all(FALSE); debug_cpu_go(info->machine, ~0); } else DestroyWindow(wnd); break; // destroy: close down the window case WM_NCDESTROY: debugwin_window_free(info); break; // everything else: defaults default: return DefWindowProc(wnd, message, wparam, lparam); } return 0; } //============================================================ // debugwin_view_create //============================================================ static int debugwin_view_create(debugwin_info *info, int which, int type) { debugview_info *view = &info->view[which]; // set the owner view->owner = info; // create the child view view->wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), NULL, DEBUG_VIEW_STYLE, 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), view); if (view->wnd == NULL) goto cleanup; // create the scroll bars view->hscroll = CreateWindowEx(HSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, HSCROLL_STYLE, 0, 0, 100, CW_USEDEFAULT, view->wnd, NULL, GetModuleHandle(NULL), view); view->vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), NULL, VSCROLL_STYLE, 0, 0, CW_USEDEFAULT, 100, view->wnd, NULL, GetModuleHandle(NULL), view); if (view->hscroll == NULL || view->vscroll == NULL) goto cleanup; // create the debug view view->view = debug_view_alloc(info->machine, type, debugwin_view_update, view); if (view->view == NULL) goto cleanup; return 1; cleanup: if (view->view) debug_view_free(view->view); if (view->hscroll) DestroyWindow(view->hscroll); if (view->vscroll) DestroyWindow(view->vscroll); if (view->wnd) DestroyWindow(view->wnd); return 0; } //============================================================ // debugwin_view_set_bounds //============================================================ static void debugwin_view_set_bounds(debugview_info *info, HWND parent, const RECT *newbounds) { RECT bounds = *newbounds; // account for the edges and set the bounds if (info->wnd) smart_set_window_bounds(info->wnd, parent, &bounds); // update debugwin_view_update(info->view, info); } //============================================================ // debugwin_view_draw_contents //============================================================ static void debugwin_view_draw_contents(debugview_info *view, HDC windc) { const debug_view_char *viewdata = debug_view_get_chars(view->view); debug_view_xy visarea = debug_view_get_visible_size(view->view); HGDIOBJ oldfont, oldbitmap; COLORREF oldfgcolor; UINT32 col, row; HBITMAP bitmap; int oldbkmode; RECT client; HDC dc; // get the client rect GetClientRect(view->wnd, &client); // create a compatible DC and an offscreen bitmap dc = CreateCompatibleDC(windc); if (dc == NULL) return; bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom); if (bitmap == NULL) { DeleteDC(dc); return; } oldbitmap = SelectObject(dc, bitmap); // set the font oldfont = SelectObject(dc, debug_font); oldfgcolor = GetTextColor(dc); oldbkmode = GetBkMode(dc); SetBkMode(dc, TRANSPARENT); // iterate over rows and columns for (row = 0; row < visarea.y; row++) { int iter; // loop twice; once to fill the background and once to draw the text for (iter = 0; iter < 2; iter++) { COLORREF fgcolor = RGB(0x00,0x00,0x00); COLORREF bgcolor = RGB(0xff,0xff,0xff); HBRUSH bgbrush = NULL; int last_attrib = -1; TCHAR buffer[256]; int count = 0; RECT bounds; // initialize the text bounds bounds.left = bounds.right = 0; bounds.top = row * debug_font_height; bounds.bottom = bounds.top + debug_font_height; // start with a brush on iteration #0 if (iter == 0) bgbrush = CreateSolidBrush(bgcolor); // iterate over columns for (col = 0; col < visarea.x; col++) { // if the attribute changed, adjust the colors if (viewdata[col].attrib != last_attrib) { COLORREF oldbg = bgcolor; // reset to standard colors fgcolor = RGB(0x00,0x00,0x00); bgcolor = RGB(0xff,0xff,0xff); // pick new fg/bg colors if (viewdata[col].attrib & DCA_ANCILLARY) bgcolor = RGB(0xe0,0xe0,0xe0); if (viewdata[col].attrib & DCA_SELECTED) bgcolor = RGB(0xff,0x80,0x80); if (viewdata[col].attrib & DCA_CURRENT) bgcolor = RGB(0xff,0xff,0x00); if ((viewdata[col].attrib & DCA_SELECTED) && (viewdata[col].attrib & DCA_CURRENT)) bgcolor = RGB(0xff,0xc0,0x80); if (viewdata[col].attrib & DCA_CHANGED) fgcolor = RGB(0xff,0x00,0x00); if (viewdata[col].attrib & DCA_INVALID) fgcolor = RGB(0x00,0x00,0xff); if (viewdata[col].attrib & DCA_DISABLED) fgcolor = RGB((GetRValue(fgcolor) + GetRValue(bgcolor)) / 2, (GetGValue(fgcolor) + GetGValue(bgcolor)) / 2, (GetBValue(fgcolor) + GetBValue(bgcolor)) / 2); if (viewdata[col].attrib & DCA_COMMENT) fgcolor = RGB(0x00,0x80,0x00); // flush any pending drawing if (count > 0) { bounds.right = bounds.left + count * debug_font_width; if (iter == 0) FillRect(dc, &bounds, bgbrush); else ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL); bounds.left = bounds.right; count = 0; } // set the new colors if (iter == 0 && oldbg != bgcolor) { DeleteObject(bgbrush); bgbrush = CreateSolidBrush(bgcolor); } else if (iter == 1) SetTextColor(dc, fgcolor); last_attrib = viewdata[col].attrib; } // add this character to the buffer buffer[count++] = viewdata[col].byte; } // flush any remaining stuff if (count > 0) { bounds.right = bounds.left + count * debug_font_width; if (iter == 0) FillRect(dc, &bounds, bgbrush); else ExtTextOut(dc, bounds.left, bounds.top, 0, NULL, buffer, count, NULL); } // erase to the end of the line if (iter == 0) { bounds.left = bounds.right; bounds.right = client.right; FillRect(dc, &bounds, bgbrush); DeleteObject(bgbrush); } } // advance viewdata viewdata += visarea.x; } // erase anything beyond the bottom with white GetClientRect(view->wnd, &client); client.top = visarea.y * debug_font_height; FillRect(dc, &client, (HBRUSH)GetStockObject(WHITE_BRUSH)); // reset the font SetBkMode(dc, oldbkmode); SetTextColor(dc, oldfgcolor); SelectObject(dc, oldfont); // blit the final results BitBlt(windc, 0, 0, client.right, client.bottom, dc, 0, 0, SRCCOPY); // undo the offscreen stuff SelectObject(dc, oldbitmap); DeleteObject(bitmap); DeleteDC(dc); } //============================================================ // debugwin_view_update //============================================================ static void debugwin_view_update(debug_view *view, void *osdprivate) { debugview_info *info = (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 = debug_view_get_total_size(view); topleft = debug_view_get_visible_position(view); // determine if we need to show the scrollbars show_vscroll = show_hscroll = FALSE; if (totalsize.x > visiblesize.x && bounds.bottom >= hscroll_height) { bounds.bottom -= hscroll_height; visiblesize.y = (bounds.bottom - bounds.top) / debug_font_height; show_hscroll = TRUE; } if (totalsize.y > visiblesize.y && bounds.right >= vscroll_width) { bounds.right -= vscroll_width; visiblesize.x = (bounds.right - bounds.left) / debug_font_width; show_vscroll = TRUE; } if (!show_vscroll && totalsize.y > visiblesize.y && bounds.right >= vscroll_width) { bounds.right -= vscroll_width; visiblesize.x = (bounds.right - bounds.left) / debug_font_width; show_vscroll = TRUE; } // compute the bounds of the scrollbars GetClientRect(info->wnd, &vscroll_bounds); vscroll_bounds.left = vscroll_bounds.right - vscroll_width; if (show_hscroll) vscroll_bounds.bottom -= hscroll_height; GetClientRect(info->wnd, &hscroll_bounds); hscroll_bounds.top = hscroll_bounds.bottom - hscroll_height; if (show_vscroll) hscroll_bounds.right -= vscroll_width; // if we hid the scrollbars, make sure we reset the top/left corners if (topleft.y + visiblesize.y > totalsize.y) topleft.y = MAX(totalsize.y - visiblesize.y, 0); if (topleft.x + visiblesize.x > totalsize.x) topleft.x = MAX(totalsize.x - visiblesize.x, 0); // fill out the scroll info struct for the vertical scrollbar scrollinfo.cbSize = sizeof(scrollinfo); scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; scrollinfo.nMin = 0; scrollinfo.nMax = totalsize.y - 1; scrollinfo.nPage = visiblesize.y; scrollinfo.nPos = topleft.y; SetScrollInfo(info->vscroll, SB_CTL, &scrollinfo, TRUE); // fill out the scroll info struct for the horizontal scrollbar scrollinfo.cbSize = sizeof(scrollinfo); scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; scrollinfo.nMin = 0; scrollinfo.nMax = totalsize.x - 1; scrollinfo.nPage = visiblesize.x; scrollinfo.nPos = topleft.x; SetScrollInfo(info->hscroll, SB_CTL, &scrollinfo, TRUE); // update window info visiblesize.y++; visiblesize.x++; debug_view_set_visible_size(view, visiblesize); debug_view_set_visible_position(view, topleft); // invalidate the bounds InvalidateRect(info->wnd, NULL, FALSE); // adjust the bounds of the scrollbars and show/hide them if (info->vscroll) { if (show_vscroll) smart_set_window_bounds(info->vscroll, info->wnd, &vscroll_bounds); smart_show_window(info->vscroll, show_vscroll); } if (info->hscroll) { if (show_hscroll) smart_set_window_bounds(info->hscroll, info->wnd, &hscroll_bounds); smart_show_window(info->hscroll, show_hscroll); } } //============================================================ // debugwin_view_process_scroll //============================================================ static UINT32 debugwin_view_process_scroll(debugview_info *info, WORD type, HWND wnd) { SCROLLINFO scrollinfo; INT32 maxval; INT32 result; // get the current info scrollinfo.cbSize = sizeof(scrollinfo); scrollinfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; GetScrollInfo(wnd, SB_CTL, &scrollinfo); // by default we stay put result = scrollinfo.nPos; // determine the maximum value maxval = (scrollinfo.nMax > scrollinfo.nPage) ? (scrollinfo.nMax - scrollinfo.nPage + 1) : 0; // handle the message switch (type) { case SB_THUMBTRACK: result = scrollinfo.nTrackPos; break; case SB_LEFT: result = 0; break; case SB_RIGHT: result = maxval; break; case SB_LINELEFT: result -= 1; break; case SB_LINERIGHT: result += 1; break; case SB_PAGELEFT: result -= scrollinfo.nPage - 1; break; case SB_PAGERIGHT: result += scrollinfo.nPage - 1; break; } // generic rangecheck if (result < 0) result = 0; if (result > maxval) result = maxval; // set the new position scrollinfo.fMask = SIF_POS; scrollinfo.nPos = result; SetScrollInfo(wnd, SB_CTL, &scrollinfo, TRUE); return (UINT32)result; } //============================================================ // debugwin_view_prev_view //============================================================ static void debugwin_view_prev_view(debugwin_info *info, debugview_info *curview) { int curindex = 1; int numviews; // count the number of views for (numviews = 0; numviews < MAX_VIEWS; numviews++) if (info->view[numviews].wnd == NULL) break; // if we have a curview, find out its index if (curview) curindex = curview - &info->view[0]; // loop until we find someone to take focus while (1) { // advance to the previous index curindex--; if (curindex < -1) curindex = numviews - 1; // negative numbers mean the focuswnd if (curindex < 0 && info->focuswnd != NULL) { SetFocus(info->focuswnd); break; } // positive numbers mean a view else if (curindex >= 0 && info->view[curindex].wnd != NULL && debug_view_get_cursor_supported(info->view[curindex].view)) { SetFocus(info->view[curindex].wnd); break; } } } //============================================================ // debugwin_view_next_view //============================================================ static void debugwin_view_next_view(debugwin_info *info, debugview_info *curview) { int curindex = -1; int numviews; // count the number of views for (numviews = 0; numviews < MAX_VIEWS; numviews++) if (info->view[numviews].wnd == NULL) break; // if we have a curview, find out its index if (curview) curindex = curview - &info->view[0]; // loop until we find someone to take focus while (1) { // advance to the previous index curindex++; if (curindex >= numviews) curindex = -1; // negative numbers mean the focuswnd if (curindex < 0 && info->focuswnd != NULL) { SetFocus(info->focuswnd); break; } // positive numbers mean a view else if (curindex >= 0 && info->view[curindex].wnd != NULL && debug_view_get_cursor_supported(info->view[curindex].view)) { SetFocus(info->view[curindex].wnd); InvalidateRect(info->view[curindex].wnd, NULL, FALSE); break; } } } //============================================================ // debugwin_view_proc //============================================================ static LRESULT CALLBACK debugwin_view_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) { debugview_info *info = (debugview_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); // handle a few messages switch (message) { // set the info pointer case WM_CREATE: { CREATESTRUCT *createinfo = (CREATESTRUCT *)lparam; SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)createinfo->lpCreateParams); break; } // paint: redraw the last bitmap case WM_PAINT: { PAINTSTRUCT pstruct; HDC dc = BeginPaint(wnd, &pstruct); debugwin_view_draw_contents(info, dc); EndPaint(wnd, &pstruct); break; } // keydown: handle debugger keys case WM_KEYDOWN: { if ((*info->owner->handle_key)(info->owner, wparam, lparam)) info->owner->ignore_char_lparam = lparam >> 16; else { switch (wparam) { case VK_UP: debug_view_type_character(info->view, DCH_UP); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_DOWN: debug_view_type_character(info->view, DCH_DOWN); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_LEFT: if (GetAsyncKeyState(VK_CONTROL) & 0x8000) debug_view_type_character(info->view, DCH_CTRLLEFT); else debug_view_type_character(info->view, DCH_LEFT); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_RIGHT: if (GetAsyncKeyState(VK_CONTROL) & 0x8000) debug_view_type_character(info->view, DCH_CTRLRIGHT); else debug_view_type_character(info->view, DCH_RIGHT); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_PRIOR: debug_view_type_character(info->view, DCH_PUP); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_NEXT: debug_view_type_character(info->view, DCH_PDOWN); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_HOME: if (GetAsyncKeyState(VK_CONTROL) & 0x8000) debug_view_type_character(info->view, DCH_CTRLHOME); else debug_view_type_character(info->view, DCH_HOME); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_END: if (GetAsyncKeyState(VK_CONTROL) & 0x8000) debug_view_type_character(info->view, DCH_CTRLEND); else debug_view_type_character(info->view, DCH_END); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_ESCAPE: if (info->owner->focuswnd != NULL) SetFocus(info->owner->focuswnd); info->owner->ignore_char_lparam = lparam >> 16; break; case VK_TAB: if (GetAsyncKeyState(VK_SHIFT) & 0x8000) debugwin_view_prev_view(info->owner, info); else debugwin_view_next_view(info->owner, info); info->owner->ignore_char_lparam = lparam >> 16; break; } } break; } // char: ignore chars associated with keys we've handled case WM_CHAR: { if (info->owner->ignore_char_lparam == (lparam >> 16)) info->owner->ignore_char_lparam = 0; else if (waiting_for_debugger || !debugwin_seq_pressed(info->owner->machine)) { if (wparam >= 32 && wparam < 127 && debug_view_get_cursor_supported(info->view)) debug_view_type_character(info->view, wparam); else return DefWindowProc(wnd, message, wparam, lparam); } break; } // gaining focus case WM_SETFOCUS: { if (debug_view_get_cursor_supported(info->view)) debug_view_set_cursor_visible(info->view, TRUE); break; } // losing focus case WM_KILLFOCUS: { if (debug_view_get_cursor_supported(info->view)) debug_view_set_cursor_visible(info->view, FALSE); break; } // mouse click case WM_LBUTTONDOWN: { if (debug_view_get_cursor_supported(info->view)) { debug_view_xy topleft = debug_view_get_visible_position(info->view); 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; debug_view_set_cursor_position(info->view, newpos); SetFocus(wnd); } break; } // hscroll case WM_HSCROLL: { debug_view_xy topleft = debug_view_get_visible_position(info->view); topleft.x = debugwin_view_process_scroll(info, LOWORD(wparam), (HWND)lparam); debug_view_set_visible_position(info->view, topleft); debug_view_flush_updates(info->owner->machine); break; } // vscroll case WM_VSCROLL: { debug_view_xy topleft = debug_view_get_visible_position(info->view); topleft.y = debugwin_view_process_scroll(info, LOWORD(wparam), (HWND)lparam); debug_view_set_visible_position(info->view, topleft); debug_view_flush_updates(info->owner->machine); break; } // everything else: defaults default: return DefWindowProc(wnd, message, wparam, lparam); } return 0; } //============================================================ // debugwin_edit_proc //============================================================ static LRESULT CALLBACK debugwin_edit_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam) { debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); TCHAR buffer[MAX_EDIT_STRING]; // handle a few messages switch (message) { // key down: handle navigation in the attached view case WM_KEYDOWN: switch (wparam) { case VK_UP: if (info->last_history < info->history_count - 1) info->last_history++; else info->last_history = 0; SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]); SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING); break; case VK_DOWN: if (info->last_history > 0) info->last_history--; else if (info->history_count > 0) info->last_history = info->history_count - 1; else info->last_history = 0; SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)&info->history[info->last_history][0]); SendMessage(wnd, EM_SETSEL, (WPARAM)MAX_EDIT_STRING, (LPARAM)MAX_EDIT_STRING); break; case VK_PRIOR: if (info->view[0].wnd && info->view[0].vscroll) SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)info->view[0].vscroll); break; case VK_NEXT: if (info->view[0].wnd && info->view[0].vscroll) SendMessage(info->view[0].wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)info->view[0].vscroll); break; case VK_TAB: if (GetAsyncKeyState(VK_SHIFT) & 0x8000) debugwin_view_prev_view(info, NULL); else debugwin_view_next_view(info, NULL); info->ignore_char_lparam = lparam >> 16; break; default: if ((*info->handle_key)(info, wparam, lparam)) info->ignore_char_lparam = lparam >> 16; else return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam); break; } break; // char: handle the return key case WM_CHAR: // ignore chars associated with keys we've handled if (info->ignore_char_lparam == (lparam >> 16)) info->ignore_char_lparam = 0; else if (waiting_for_debugger || !debugwin_seq_pressed(info->machine)) { switch (wparam) { case 13: { // fetch the text SendMessage(wnd, WM_GETTEXT, (WPARAM)ARRAY_LENGTH(buffer), (LPARAM)buffer); // add to the history if it's not a repeat of the last one if (buffer[0] != 0 && _tcscmp(buffer, &info->history[0][0])) { memmove(&info->history[1][0], &info->history[0][0], (HISTORY_LENGTH - 1) * MAX_EDIT_STRING); _tcscpy(&info->history[0][0], buffer); if (info->history_count < HISTORY_LENGTH) info->history_count++; } info->last_history = info->history_count - 1; // process if (info->process_string) { char *utf8_buffer = utf8_from_tstring(buffer); if (utf8_buffer != NULL) { (*info->process_string)(info, utf8_buffer); free(utf8_buffer); } } break; } case 27: { // fetch the text SendMessage(wnd, WM_GETTEXT, (WPARAM)sizeof(buffer), (LPARAM)buffer); // if it's not empty, clear the text if (_tcslen(buffer) > 0) { info->ignore_char_lparam = lparam >> 16; SendMessage(wnd, WM_SETTEXT, (WPARAM)0, (LPARAM)info->edit_defstr); } break; } default: return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam); } } break; // everything else: defaults default: return CallWindowProc(info->original_editproc, wnd, message, wparam, lparam); } return 0; } //============================================================ // generic_create_window //============================================================ #ifdef UNUSED_FUNCTION static void generic_create_window(running_machine *machine, int type) { debugwin_info *info; char title[256]; // create the window _snprintf(title, ARRAY_LENGTH(title), "Debug: %s [%s]", machine->gamedrv->description, machine->gamedrv->name); info = debugwin_window_create(machine, title, NULL); if (info == NULL || !debugwin_view_create(info, 0, type)) return; // set the child function info->recompute_children = generic_recompute_children; // recompute the children once to get the maxwidth generic_recompute_children(info); // position the window and recompute children again SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW); generic_recompute_children(info); } #endif //============================================================ // generic_recompute_children //============================================================ static void generic_recompute_children(debugwin_info *info) { debug_view_xy totalsize = debug_view_get_total_size(info->view[0].view); RECT parent; RECT bounds; // compute a client rect bounds.top = bounds.left = 0; bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; bounds.bottom = 200; AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); // clamp the min/max size info->maxwidth = bounds.right - bounds.left; // get the parent's dimensions GetClientRect(info->wnd, &parent); // view gets the remaining space InflateRect(&parent, -EDGE_WIDTH, -EDGE_WIDTH); debugwin_view_set_bounds(&info->view[0], info->wnd, &parent); } //============================================================ // log_create_window //============================================================ static void log_create_window(running_machine *machine) { debug_view_xy totalsize; debugwin_info *info; char title[256]; RECT bounds; // create the window _snprintf(title, ARRAY_LENGTH(title), "Errorlog: %s [%s]", machine->gamedrv->description, machine->gamedrv->name); info = debugwin_window_create(machine, title, NULL); if (info == NULL || !debugwin_view_create(info, 0, DVT_LOG)) return; // set the child function info->recompute_children = generic_recompute_children; // get the view width totalsize = debug_view_get_total_size(info->view[0].view); // compute a client rect bounds.top = bounds.left = 0; bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; bounds.bottom = 200; AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); // clamp the min/max size info->maxwidth = bounds.right - bounds.left; // position the window at the bottom-right SetWindowPos(info->wnd, HWND_TOP, 100, 100, bounds.right - bounds.left, bounds.bottom - bounds.top, SWP_SHOWWINDOW); // recompute the children and set focus on the edit box generic_recompute_children(info); } //============================================================ // memory_create_window //============================================================ static void memory_create_window(running_machine *machine) { const device_config *curcpu = debug_cpu_get_visible_cpu(machine); const memory_subview_item *subview; debugwin_info *info; HMENU optionsmenu; int cursel = -1; // create the window info = debugwin_window_create(machine, "Memory", NULL); if (info == NULL || !debugwin_view_create(info, 0, DVT_MEMORY)) return; // set the handlers info->handle_command = memory_handle_command; info->handle_key = memory_handle_key; info->update_menu = memory_update_menu; // create the options menu optionsmenu = CreatePopupMenu(); AppendMenu(optionsmenu, MF_ENABLED, ID_1_BYTE_CHUNKS, TEXT("1-byte chunks\tCtrl+1")); AppendMenu(optionsmenu, MF_ENABLED, ID_2_BYTE_CHUNKS, TEXT("2-byte chunks\tCtrl+2")); AppendMenu(optionsmenu, MF_ENABLED, ID_4_BYTE_CHUNKS, TEXT("4-byte chunks\tCtrl+4")); AppendMenu(optionsmenu, MF_ENABLED, ID_8_BYTE_CHUNKS, TEXT("8-byte chunks\tCtrl+8")); AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); AppendMenu(optionsmenu, MF_ENABLED, ID_LOGICAL_ADDRESSES, TEXT("Logical Addresses\tCtrl+L")); AppendMenu(optionsmenu, MF_ENABLED, ID_PHYSICAL_ADDRESSES, TEXT("Physical Addresses\tCtrl+Y")); AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); AppendMenu(optionsmenu, MF_ENABLED, ID_REVERSE_VIEW, TEXT("Reverse View\tCtrl+R")); AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); AppendMenu(optionsmenu, MF_ENABLED, ID_INCREASE_MEM_WIDTH, TEXT("Increase bytes per line\tCtrl+P")); AppendMenu(optionsmenu, MF_ENABLED, ID_DECREASE_MEM_WIDTH, TEXT("Decrease bytes per line\tCtrl+O")); AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options")); // set up the view to track the initial expression memory_view_set_expression(info->view[0].view, "0"); strcpy(info->edit_defstr, "0"); // create an edit box and override its key handling info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE, 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL); info->original_editproc = (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 for (subview = memory_view_get_subview_list(info->view[0].view); subview != NULL; subview = subview->next) { TCHAR *t_name = tstring_from_utf8(subview->name); int item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)t_name); free(t_name); if (cursel == -1 && subview->space != NULL && subview->space->cpu == curcpu) cursel = item; } if (cursel == -1) cursel = 0; SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0); memory_view_set_subview(info->view[0].view, cursel); // set the child functions info->recompute_children = memory_recompute_children; info->process_string = memory_process_string; // set the caption memory_update_caption(machine, info->wnd); // recompute the children once to get the maxwidth memory_recompute_children(info); // position the window and recompute children again SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW); memory_recompute_children(info); // mark the edit box as the default focus and set it info->focuswnd = info->editwnd; SetFocus(info->editwnd); } //============================================================ // memory_recompute_children //============================================================ static void memory_recompute_children(debugwin_info *info) { debug_view_xy totalsize = debug_view_get_total_size(info->view[0].view); RECT parent, memrect, editrect, comborect; RECT bounds; // compute a client rect bounds.top = bounds.left = 0; bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; bounds.bottom = 200; AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); // clamp the min/max size info->maxwidth = bounds.right - bounds.left; // get the parent's dimensions GetClientRect(info->wnd, &parent); // edit box gets half of the width editrect.top = parent.top + EDGE_WIDTH; editrect.bottom = editrect.top + debug_font_height + 4; editrect.left = parent.left + EDGE_WIDTH; editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH; // combo box gets the other half of the width comborect.top = editrect.top; comborect.bottom = editrect.bottom; comborect.left = editrect.right + 2 * EDGE_WIDTH; comborect.right = parent.right - EDGE_WIDTH; // memory view gets the rest memrect.top = editrect.bottom + 2 * EDGE_WIDTH; memrect.bottom = parent.bottom - EDGE_WIDTH; memrect.left = parent.left + EDGE_WIDTH; memrect.right = parent.right - EDGE_WIDTH; // set the bounds of things debugwin_view_set_bounds(&info->view[0], info->wnd, &memrect); smart_set_window_bounds(info->editwnd, info->wnd, &editrect); smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect); } //============================================================ // memory_process_string //============================================================ static void memory_process_string(debugwin_info *info, const char *string) { // set the string to the memory view memory_view_set_expression(info->view[0].view, string); // select everything in the edit text box SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); // update the default string to match strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1); } //============================================================ // memory_update_menu //============================================================ static void memory_update_menu(debugwin_info *info) { CheckMenuItem(GetMenu(info->wnd), ID_1_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 1 ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_2_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 2 ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_4_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 4 ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_8_BYTE_CHUNKS, MF_BYCOMMAND | (memory_view_get_bytes_per_chunk(info->view[0].view) == 8 ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_LOGICAL_ADDRESSES, MF_BYCOMMAND | (memory_view_get_physical(info->view[0].view) ? MF_UNCHECKED : MF_CHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_PHYSICAL_ADDRESSES, MF_BYCOMMAND | (memory_view_get_physical(info->view[0].view) ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_REVERSE_VIEW, MF_BYCOMMAND | (memory_view_get_reverse(info->view[0].view) ? MF_CHECKED : MF_UNCHECKED)); } //============================================================ // memory_handle_command //============================================================ static int memory_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam) { switch (HIWORD(wparam)) { // combo box selection changed case CBN_SELCHANGE: { int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0); if (sel != CB_ERR) { memory_view_set_subview(info->view[0].view, sel); memory_update_caption(info->machine, info->wnd); // reset the focus SetFocus(info->focuswnd); return 1; } break; } // menu selections case 0: switch (LOWORD(wparam)) { case ID_1_BYTE_CHUNKS: memory_view_set_bytes_per_chunk(info->view[0].view, 1); return 1; case ID_2_BYTE_CHUNKS: memory_view_set_bytes_per_chunk(info->view[0].view, 2); return 1; case ID_4_BYTE_CHUNKS: memory_view_set_bytes_per_chunk(info->view[0].view, 4); return 1; case ID_8_BYTE_CHUNKS: memory_view_set_bytes_per_chunk(info->view[0].view, 8); return 1; case ID_LOGICAL_ADDRESSES: memory_view_set_physical(info->view[0].view, FALSE); return 1; case ID_PHYSICAL_ADDRESSES: memory_view_set_physical(info->view[0].view, TRUE); return 1; case ID_REVERSE_VIEW: memory_view_set_reverse(info->view[0].view, !memory_view_get_reverse(info->view[0].view)); return 1; case ID_INCREASE_MEM_WIDTH: memory_view_set_chunks_per_row(info->view[0].view, memory_view_get_chunks_per_row(info->view[0].view) + 1); return 1; case ID_DECREASE_MEM_WIDTH: memory_view_set_chunks_per_row(info->view[0].view, memory_view_get_chunks_per_row(info->view[0].view) - 1); return 1; } break; } return global_handle_command(info, wparam, lparam); } //============================================================ // memory_handle_key //============================================================ static int memory_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam) { if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { switch (wparam) { case '1': SendMessage(info->wnd, WM_COMMAND, ID_1_BYTE_CHUNKS, 0); return 1; case '2': SendMessage(info->wnd, WM_COMMAND, ID_2_BYTE_CHUNKS, 0); return 1; case '4': SendMessage(info->wnd, WM_COMMAND, ID_4_BYTE_CHUNKS, 0); return 1; case '8': SendMessage(info->wnd, WM_COMMAND, ID_8_BYTE_CHUNKS, 0); return 1; case 'L': SendMessage(info->wnd, WM_COMMAND, ID_LOGICAL_ADDRESSES, 0); return 1; case 'Y': SendMessage(info->wnd, WM_COMMAND, ID_PHYSICAL_ADDRESSES, 0); return 1; case 'R': SendMessage(info->wnd, WM_COMMAND, ID_REVERSE_VIEW, 0); return 1; case 'P': SendMessage(info->wnd, WM_COMMAND, ID_INCREASE_MEM_WIDTH, 0); return 1; case 'O': SendMessage(info->wnd, WM_COMMAND, ID_DECREASE_MEM_WIDTH, 0); return 1; } } return global_handle_key(info, wparam, lparam); } //============================================================ // memory_update_caption //============================================================ static void memory_update_caption(running_machine *machine, HWND wnd) { debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); const memory_subview_item *subview = memory_view_get_current_subview(info->view[0].view); char title[256]; sprintf(title, "Memory: %s", subview->name); win_set_window_text_utf8(wnd, title); } //============================================================ // disasm_create_window //============================================================ static void disasm_create_window(running_machine *machine) { const device_config *curcpu = debug_cpu_get_visible_cpu(machine); const disasm_subview_item *subview; debugwin_info *info; HMENU optionsmenu; int cursel = 0; // create the window info = debugwin_window_create(machine, "Disassembly", NULL); if (info == NULL || !debugwin_view_create(info, 0, DVT_DISASSEMBLY)) return; // create the options menu optionsmenu = CreatePopupMenu(); AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9")); AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4")); AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+M")); AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options")); // set the handlers info->handle_command = disasm_handle_command; info->handle_key = disasm_handle_key; info->update_menu = disasm_update_menu; // set up the view to track the initial expression disasm_view_set_expression(info->view[0].view, "curpc"); strcpy(info->edit_defstr, "curpc"); // create an edit box and override its key handling info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE, 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL); info->original_editproc = (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 for (subview = disasm_view_get_subview_list(info->view[0].view); subview != NULL; subview = subview->next) { TCHAR *t_name = tstring_from_utf8(subview->name); int item = SendMessage(info->otherwnd[0], CB_ADDSTRING, 0, (LPARAM)t_name); free(t_name); if (cursel == 0 && subview->space->cpu == curcpu) cursel = item; } SendMessage(info->otherwnd[0], CB_SETCURSEL, cursel, 0); disasm_view_set_subview(info->view[0].view, cursel); // set the child functions info->recompute_children = disasm_recompute_children; info->process_string = disasm_process_string; // set the caption disasm_update_caption(machine, info->wnd); // recompute the children once to get the maxwidth disasm_recompute_children(info); // position the window and recompute children again SetWindowPos(info->wnd, HWND_TOP, 100, 100, info->maxwidth, 200, SWP_SHOWWINDOW); disasm_recompute_children(info); // mark the edit box as the default focus and set it info->focuswnd = info->editwnd; SetFocus(info->editwnd); } //============================================================ // disasm_recompute_children //============================================================ static void disasm_recompute_children(debugwin_info *info) { debug_view_xy totalsize = debug_view_get_total_size(info->view[0].view); RECT parent, dasmrect, editrect, comborect; RECT bounds; // compute a client rect bounds.top = bounds.left = 0; bounds.right = totalsize.x * debug_font_width + vscroll_width + 2 * EDGE_WIDTH; bounds.bottom = 200; AdjustWindowRectEx(&bounds, DEBUG_WINDOW_STYLE, FALSE, DEBUG_WINDOW_STYLE_EX); // clamp the min/max size info->maxwidth = bounds.right - bounds.left; // get the parent's dimensions GetClientRect(info->wnd, &parent); // edit box gets half of the width editrect.top = parent.top + EDGE_WIDTH; editrect.bottom = editrect.top + debug_font_height + 4; editrect.left = parent.left + EDGE_WIDTH; editrect.right = parent.left + (parent.right - parent.left) / 2 - EDGE_WIDTH; // combo box gets the other half of the width comborect.top = editrect.top; comborect.bottom = editrect.bottom; comborect.left = editrect.right + 2 * EDGE_WIDTH; comborect.right = parent.right - EDGE_WIDTH; // disasm view gets the rest dasmrect.top = editrect.bottom + 2 * EDGE_WIDTH; dasmrect.bottom = parent.bottom - EDGE_WIDTH; dasmrect.left = parent.left + EDGE_WIDTH; dasmrect.right = parent.right - EDGE_WIDTH; // set the bounds of things debugwin_view_set_bounds(&info->view[0], info->wnd, &dasmrect); smart_set_window_bounds(info->editwnd, info->wnd, &editrect); smart_set_window_bounds(info->otherwnd[0], info->wnd, &comborect); } //============================================================ // disasm_process_string //============================================================ static void disasm_process_string(debugwin_info *info, const char *string) { // set the string to the disasm view disasm_view_set_expression(info->view[0].view, string); // select everything in the edit text box SendMessage(info->editwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1); // update the default string to match strncpy(info->edit_defstr, string, sizeof(info->edit_defstr) - 1); } //============================================================ // disasm_update_menu //============================================================ static void disasm_update_menu(debugwin_info *info) { disasm_right_column rightcol = disasm_view_get_right_column(info->view[0].view); CheckMenuItem(GetMenu(info->wnd), ID_SHOW_RAW, MF_BYCOMMAND | (rightcol == DASM_RIGHTCOL_RAW ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_SHOW_ENCRYPTED, MF_BYCOMMAND | (rightcol == DASM_RIGHTCOL_ENCRYPTED ? MF_CHECKED : MF_UNCHECKED)); CheckMenuItem(GetMenu(info->wnd), ID_SHOW_COMMENTS, MF_BYCOMMAND | (rightcol == DASM_RIGHTCOL_COMMENTS ? MF_CHECKED : MF_UNCHECKED)); } //============================================================ // disasm_handle_command //============================================================ static int disasm_handle_command(debugwin_info *info, WPARAM wparam, LPARAM lparam) { char command[64]; switch (HIWORD(wparam)) { // combo box selection changed case CBN_SELCHANGE: { int sel = SendMessage((HWND)lparam, CB_GETCURSEL, 0, 0); if (sel != CB_ERR) { disasm_view_set_subview(info->view[0].view, sel); disasm_update_caption(info->machine, info->wnd); // reset the focus SetFocus(info->focuswnd); return 1; } break; } // menu selections case 0: switch (LOWORD(wparam)) { case ID_SHOW_RAW: disasm_view_set_right_column(info->view[0].view, DASM_RIGHTCOL_RAW); (*info->recompute_children)(info); return 1; case ID_SHOW_ENCRYPTED: disasm_view_set_right_column(info->view[0].view, DASM_RIGHTCOL_ENCRYPTED); (*info->recompute_children)(info); return 1; case ID_SHOW_COMMENTS: disasm_view_set_right_column(info->view[0].view, DASM_RIGHTCOL_COMMENTS); (*info->recompute_children)(info); return 1; case ID_RUN_TO_CURSOR: if (debug_view_get_cursor_visible(info->view[0].view)) { const address_space *space = disasm_view_get_current_subview(info->view[0].view)->space; if (debug_cpu_get_visible_cpu(info->machine) == space->cpu) { offs_t address = disasm_view_get_selected_address(info->view[0].view); sprintf(command, "go %X", address); debug_console_execute_command(info->machine, command, 1); } } return 1; case ID_TOGGLE_BREAKPOINT: if (debug_view_get_cursor_visible(info->view[0].view)) { const address_space *space = disasm_view_get_current_subview(info->view[0].view)->space; if (debug_cpu_get_visible_cpu(info->machine) == space->cpu) { offs_t address = disasm_view_get_selected_address(info->view[0].view); cpu_debug_data *cpuinfo = cpu_get_debug_data(space->cpu); debug_cpu_breakpoint *bp; INT32 bpindex = -1; /* first find an existing breakpoint at this address */ for (bp = cpuinfo->bplist; bp != NULL; bp = bp->next) if (address == bp->address) { bpindex = bp->index; break; } /* if it doesn't exist, add a new one */ if (bpindex == -1) sprintf(command, "bpset 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 (debug_view_get_cursor_visible(info->view[0].view)) { SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0); return 1; } break; } return global_handle_key(info, wparam, lparam); } //============================================================ // disasm_update_caption //============================================================ static void disasm_update_caption(running_machine *machine, HWND wnd) { debugwin_info *info = (debugwin_info *)(FPTR)GetWindowLongPtr(wnd, GWLP_USERDATA); const disasm_subview_item *subview = disasm_view_get_current_subview(info->view[0].view); char title[256]; sprintf(title, "Disassembly: %s", subview->name); win_set_window_text_utf8(wnd, title); } //============================================================ // console_create_window //============================================================ void console_create_window(running_machine *machine) { const registers_subview_item *regsubview; const disasm_subview_item *dasmsubview; debugwin_info *info; int bestwidth, bestheight; RECT bounds, work_bounds; HMENU optionsmenu; 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_REGISTERS)) goto cleanup; if (!debugwin_view_create(info, 2, DVT_CONSOLE)) goto cleanup; // create the options menu optionsmenu = CreatePopupMenu(); AppendMenu(optionsmenu, MF_ENABLED, ID_TOGGLE_BREAKPOINT, TEXT("Set breakpoint at cursor\tF9")); AppendMenu(optionsmenu, MF_ENABLED, ID_RUN_TO_CURSOR, TEXT("Run to cursor\tF4")); AppendMenu(optionsmenu, MF_DISABLED | MF_SEPARATOR, 0, TEXT("")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_RAW, TEXT("Raw opcodes\tCtrl+R")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_ENCRYPTED, TEXT("Encrypted opcodes\tCtrl+E")); AppendMenu(optionsmenu, MF_ENABLED, ID_SHOW_COMMENTS, TEXT("Comments\tCtrl+N")); AppendMenu(GetMenu(info->wnd), MF_ENABLED | MF_POPUP, (UINT_PTR)optionsmenu, TEXT("Options")); // set the handlers info->handle_command = disasm_handle_command; info->handle_key = disasm_handle_key; // set up the disassembly view to track the current pc disasm_view_set_expression(info->view[0].view, "curpc"); // create an edit box and override its key handling info->editwnd = CreateWindowEx(EDIT_BOX_STYLE_EX, TEXT("EDIT"), NULL, EDIT_BOX_STYLE, 0, 0, 100, 100, info->wnd, NULL, GetModuleHandle(NULL), NULL); info->original_editproc = (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 (regsubview = registers_view_get_subview_list(info->view[1].view); regsubview != NULL; regsubview = regsubview->next) { UINT32 regchars; // set the view and fetch the width registers_view_set_subview(info->view[1].view, regsubview->index); regchars = debug_view_get_total_size(info->view[1].view).x; // track the maximum main_console_regwidth = MAX(regchars, main_console_regwidth); } // determine the width of the console (this is fixed) conchars = debug_view_get_total_size(info->view[2].view).x; // loop over all CPUs and compute the width range based on dasm width info->minwidth = 0; info->maxwidth = 0; for (dasmsubview = disasm_view_get_subview_list(info->view[0].view); dasmsubview != NULL; dasmsubview = dasmsubview->next) { UINT32 minwidth, maxwidth, dischars; // set the view and fetch the width disasm_view_set_subview(info->view[0].view, dasmsubview->index); dischars = debug_view_get_total_size(info->view[0].view).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)); // mark the edit box as the default focus and set it info->focuswnd = info->editwnd; SetFocus(info->editwnd); return; cleanup: if (info->view[2].view) debug_view_free(info->view[2].view); if (info->view[1].view) debug_view_free(info->view[1].view); if (info->view[0].view) debug_view_free(info->view[0].view); } //============================================================ // console_recompute_children //============================================================ static void console_recompute_children(debugwin_info *info) { RECT parent, regrect, disrect, conrect, editrect; UINT32 regchars, dischars, conchars; // get the parent's dimensions GetClientRect(info->wnd, &parent); // get the total width of all three children dischars = debug_view_get_total_size(info->view[0].view).x; regchars = main_console_regwidth; conchars = debug_view_get_total_size(info->view[2].view).x; // registers always get their desired width, and span the entire height regrect.top = parent.top + EDGE_WIDTH; regrect.bottom = parent.bottom - EDGE_WIDTH; regrect.left = parent.left + EDGE_WIDTH; regrect.right = regrect.left + debug_font_width * regchars + vscroll_width; // edit box goes at the bottom of the remaining area editrect.bottom = parent.bottom - EDGE_WIDTH; editrect.top = editrect.bottom - debug_font_height - 4; editrect.left = regrect.right + EDGE_WIDTH * 2; editrect.right = parent.right - EDGE_WIDTH; // console and disassembly split the difference disrect.top = parent.top + EDGE_WIDTH; disrect.bottom = (editrect.top - parent.top) / 2 - EDGE_WIDTH; disrect.left = regrect.right + EDGE_WIDTH * 2; disrect.right = parent.right - EDGE_WIDTH; conrect.top = disrect.bottom + EDGE_WIDTH * 2; conrect.bottom = editrect.top - EDGE_WIDTH; conrect.left = regrect.right + EDGE_WIDTH * 2; conrect.right = parent.right - EDGE_WIDTH; // set the bounds of things debugwin_view_set_bounds(&info->view[0], info->wnd, &disrect); debugwin_view_set_bounds(&info->view[1], info->wnd, ®rect); debugwin_view_set_bounds(&info->view[2], info->wnd, &conrect); smart_set_window_bounds(info->editwnd, info->wnd, &editrect); } //============================================================ // console_process_string //============================================================ static void console_process_string(debugwin_info *info, const char *string) { TCHAR buffer = 0; // an empty string is a single step if (string[0] == 0) debug_cpu_single_step(info->machine, 1); // otherwise, just process the command else debug_console_execute_command(info->machine, string, 1); // clear the edit text box SendMessage(info->editwnd, WM_SETTEXT, 0, (LPARAM)&buffer); } //============================================================ // console_set_cpu //============================================================ static void console_set_cpu(const device_config *device) { const registers_subview_item *regsubitem = NULL; const disasm_subview_item *dasmsubitem; char title[256], curtitle[256]; // first set all the views to the new cpu number if (main_console->view[0].view != NULL) for (dasmsubitem = disasm_view_get_subview_list(main_console->view[0].view); dasmsubitem != NULL; dasmsubitem = dasmsubitem->next) if (dasmsubitem->space->cpu == device) { disasm_view_set_subview(main_console->view[0].view, dasmsubitem->index); break; } if (main_console->view[1].view != NULL) for (regsubitem = registers_view_get_subview_list(main_console->view[1].view); regsubitem != NULL; regsubitem = regsubitem->next) if (regsubitem->device == device) { registers_view_set_subview(main_console->view[1].view, regsubitem->index); break; } // then update the caption if (regsubitem != NULL) { snprintf(title, ARRAY_LENGTH(title), "Debug: %s - %s", device->machine->gamedrv->name, regsubitem->name); win_get_window_text_utf8(main_console->wnd, curtitle, ARRAY_LENGTH(curtitle)); if (strcmp(title, curtitle) != 0) win_set_window_text_utf8(main_console->wnd, title); } // 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_go(info->machine, ~0); return 1; case ID_NEXT_CPU: debug_cpu_next_cpu(info->machine); return 1; case ID_RUN_VBLANK: debug_cpu_go_vblank(info->machine); return 1; case ID_RUN_IRQ: debug_cpu_go_interrupt(info->machine, -1); return 1; case ID_STEP: debug_cpu_single_step(info->machine, 1); return 1; case ID_STEP_OVER: debug_cpu_single_step_over(info->machine, 1); return 1; case ID_STEP_OUT: debug_cpu_single_step_out(info->machine); return 1; case ID_HARD_RESET: mame_schedule_hard_reset(info->machine); return 1; case ID_SOFT_RESET: mame_schedule_soft_reset(info->machine); debug_cpu_go(info->machine, ~0); return 1; case ID_EXIT: if (info->focuswnd != NULL) SetFocus(info->focuswnd); mame_schedule_exit(info->machine); return 1; } return 0; } //============================================================ // global_handle_key //============================================================ static int global_handle_key(debugwin_info *info, WPARAM wparam, LPARAM lparam) { /* ignore any keys that are received while the debug key is down */ if (!waiting_for_debugger && debugwin_seq_pressed(info->machine)) return 1; switch (wparam) { case VK_F3: if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(info->wnd, WM_COMMAND, ID_HARD_RESET, 0); else SendMessage(info->wnd, WM_COMMAND, ID_SOFT_RESET, 0); return 1; case VK_F4: if (GetAsyncKeyState(VK_MENU) & 0x8000) { /* ajg - never gets here since 'alt' seems to be captured somewhere else - menu maybe? */ SendMessage(info->wnd, WM_COMMAND, ID_EXIT, 0); return 1; } break; case VK_F5: SendMessage(info->wnd, WM_COMMAND, ID_RUN, 0); return 1; case VK_F6: SendMessage(info->wnd, WM_COMMAND, ID_NEXT_CPU, 0); return 1; case VK_F7: SendMessage(info->wnd, WM_COMMAND, ID_RUN_IRQ, 0); return 1; case VK_F8: SendMessage(info->wnd, WM_COMMAND, ID_RUN_VBLANK, 0); return 1; case VK_F10: SendMessage(info->wnd, WM_COMMAND, ID_STEP_OVER, 0); return 1; case VK_F11: if (GetAsyncKeyState(VK_SHIFT) & 0x8000) SendMessage(info->wnd, WM_COMMAND, ID_STEP_OUT, 0); else SendMessage(info->wnd, WM_COMMAND, ID_STEP, 0); return 1; case VK_F12: SendMessage(info->wnd, WM_COMMAND, ID_RUN_AND_HIDE, 0); return 1; case 'M': if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { SendMessage(info->wnd, WM_COMMAND, ID_NEW_MEMORY_WND, 0); return 1; } break; case 'D': if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { SendMessage(info->wnd, WM_COMMAND, ID_NEW_DISASM_WND, 0); return 1; } break; case 'L': if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { SendMessage(info->wnd, WM_COMMAND, ID_NEW_LOG_WND, 0); return 1; } break; } return 0; } //============================================================ // smart_set_window_bounds //============================================================ static void smart_set_window_bounds(HWND wnd, HWND parent, RECT *bounds) { RECT curbounds; int flags = 0; // first get the current bounds, relative to the parent GetWindowRect(wnd, &curbounds); if (parent != NULL) { RECT parentbounds; GetWindowRect(parent, &parentbounds); curbounds.top -= parentbounds.top; curbounds.bottom -= parentbounds.top; curbounds.left -= parentbounds.left; curbounds.right -= parentbounds.left; } // if the position matches, don't change it if (curbounds.top == bounds->top && curbounds.left == bounds->left) flags |= SWP_NOMOVE; if ((curbounds.bottom - curbounds.top) == (bounds->bottom - bounds->top) && (curbounds.right - curbounds.left) == (bounds->right - bounds->left)) flags |= SWP_NOSIZE; // if we need to, reposition the window if (flags != (SWP_NOMOVE | SWP_NOSIZE)) SetWindowPos(wnd, NULL, bounds->left, bounds->top, bounds->right - bounds->left, bounds->bottom - bounds->top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | flags); } //============================================================ // smart_show_window //============================================================ static void smart_show_window(HWND wnd, BOOL show) { BOOL visible = IsWindowVisible(wnd); if ((visible && !show) || (!visible && show)) ShowWindow(wnd, show ? SW_SHOW : SW_HIDE); } //============================================================ // smart_show_all //============================================================ static void smart_show_all(BOOL show) { debugwin_info *info; if (!show) SetForegroundWindow(win_window_list->hwnd); for (info = window_list; info != NULL; info = info->next) smart_show_window(info->wnd, show); }