/* * TilEm II * * Copyright (c) 2011-2012 Benjamin Moody * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "gui.h" #include "disasmview.h" G_DEFINE_TYPE(TilemDisasmView, tilem_disasm_view, GTK_TYPE_TREE_VIEW); /* This is a HORRIBLE kludge. Don't ever do anything like this. ;) We want a widget that has the look and feel of a GtkTreeView. But our "data model" isn't consistent with a GtkTreeModel, since it changes depending on where we are. This widget keeps track of how high each row will be once rendered, and uses that to construct a GtkListStore with the appropriate number of rows to fill the window. We also override the move-cursor signal so that we can handle the boundaries. */ /* Model columns */ enum { COL_POSITION, COL_ADDRESS, COL_MNEMONIC, COL_ARGUMENTS, COL_SHOW_MNEMONIC, COL_ICON, NUM_COLUMNS }; static GtkTreeViewClass *parent_class; /* We define two "positions" for each actual address; the second is used if there's a label to be displayed at that address. */ #define POS_TO_ADDR(x) ((x) >> 1) #define ADDR_TO_POS(x) ((x) << 1) /* Disassembly */ /* Convert physical to logical address; if address is not currently mapped, use the bank-A address */ static dword default_ptol(TilemCalc *calc, dword addr) { dword addr_l; g_return_val_if_fail(calc != NULL, 0); addr_l = (*calc->hw.mem_ptol)(calc, addr); if (addr_l == 0xffffffff) addr_l = (addr & 0x3fff) | 0x4000; return addr_l; } /* Check for a label at the given address (physical or logical depending on the mode of the DisasmView) */ static const char *get_label(TilemDisasmView *dv, TilemCalc *calc, dword addr) { g_return_val_if_fail(calc != NULL, NULL); g_return_val_if_fail(dv->dbg->dasm != NULL, NULL); if (!dv->use_logical) addr = default_ptol(calc, addr); return tilem_disasm_get_label_at_address(dv->dbg->dasm, addr); } /* Disassemble a line */ static void disassemble(TilemDisasmView *dv, TilemCalc *calc, dword pos, dword *nextpos, char **mnemonic, char **args) { dword addr = POS_TO_ADDR(pos); const char *lbl; char buf[500], *p; g_return_if_fail(calc != NULL); g_return_if_fail(dv->dbg->dasm != NULL); if (!(pos & 1) && (lbl = get_label(dv, calc, addr))) { if (mnemonic) { *mnemonic = NULL; *args = g_strdup_printf("%s:", lbl); } if (nextpos) *nextpos = pos + 1; } else if (mnemonic) { tilem_disasm_disassemble(dv->dbg->dasm, calc, !dv->use_logical, addr, &addr, buf, sizeof(buf)); p = strchr(buf, '\t'); if (p) { *mnemonic = g_strndup(buf, p - buf); *args = g_strdup(p + 1); } else { *mnemonic = g_strdup(buf); *args = NULL; } if (nextpos) *nextpos = ADDR_TO_POS(addr); } else { tilem_disasm_disassemble(dv->dbg->dasm, calc, !dv->use_logical, addr, &addr, NULL, 0); if (nextpos) *nextpos = ADDR_TO_POS(addr); } } /* Get "next" position */ static dword get_next_pos(TilemDisasmView *dv, TilemCalc *calc, dword pos) { disassemble(dv, calc, pos, &pos, NULL, NULL); return pos; } /* Get "previous" position */ static dword get_prev_pos(TilemDisasmView *dv, TilemCalc *calc, dword pos) { dword addr = POS_TO_ADDR(pos); g_return_val_if_fail(calc != NULL, 0); if (pos & 1) { return pos - 1; } else { if (addr > 0) addr--; else if (dv->use_logical) addr = 0xffff; else addr = (calc->hw.romsize + calc->hw.ramsize - 1); if (get_label(dv, calc, addr)) return ADDR_TO_POS(addr) + 1; else return ADDR_TO_POS(addr); } } /* Convert physical to logical position */ static dword pos_ptol(TilemCalc *calc, dword pos) { dword addr; g_return_val_if_fail(calc != NULL, 0); if (pos == (dword) -1) return pos; addr = default_ptol(calc, POS_TO_ADDR(pos)); return ADDR_TO_POS(addr) + (pos & 1); } /* Convert logical to physical position */ static dword pos_ltop(TilemCalc *calc, dword pos) { dword addr; g_return_val_if_fail(calc != NULL, 0); if (pos == (dword) -1) return pos; addr = (*calc->hw.mem_ltop)(calc, POS_TO_ADDR(pos)); return ADDR_TO_POS(addr) + (pos & 1); } /* Icons */ static GdkPixbuf *get_icon(TilemDisasmView *dv, gboolean ispc, gboolean isbp) { const char *name; if (ispc && isbp) name = "tilem-disasm-break-pc"; else if (isbp) name = "tilem-disasm-break"; else if (ispc) name = "tilem-disasm-pc"; else return NULL; return gtk_widget_render_icon(GTK_WIDGET(dv), name, GTK_ICON_SIZE_MENU, NULL); } /* List model management */ /* Create a new list store for disassembly */ static GtkTreeModel * new_dasm_model() { GtkListStore *store; g_assert(NUM_COLUMNS == 6); store = gtk_list_store_new(6, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF); return GTK_TREE_MODEL(store); } /* Append dummy data to the model; used for sizing */ static void append_dummy_line(TilemDisasmView *dv, GtkTreeModel *model, GtkTreeIter *iter) { GtkTreeIter iter1; GdkPixbuf *icon; gtk_list_store_append(GTK_LIST_STORE(model), &iter1); icon = get_icon(dv, TRUE, FALSE); gtk_list_store_set(GTK_LIST_STORE(model), &iter1, COL_ICON, icon, COL_ADDRESS, "DD:DDDD", COL_MNEMONIC, "ROM_CALL", COL_ARGUMENTS, "_fnord", COL_SHOW_MNEMONIC, TRUE, -1); if (icon) g_object_unref(icon); if (iter) *iter = iter1; } /* Check if given logical address is a breakpoint (according to current mapping) */ static TilemDebugBreakpoint *find_bp_logical(TilemDebugger *dbg, TilemCalc *calc, dword addr) { GSList *l; TilemDebugBreakpoint *bp; dword pa = (*calc->hw.mem_ltop)(calc, addr); for (l = dbg->breakpoints; l; l = l->next) { bp = l->data; if (!(bp->mode & TILEM_DB_BREAK_EXEC)) continue; if (bp->type == TILEM_DB_BREAK_LOGICAL && bp->start <= addr && bp->end >= addr && !bp->disabled) return bp; if (bp->type == TILEM_DB_BREAK_PHYSICAL && bp->start <= pa && bp->end >= pa && !bp->disabled) return bp; } return NULL; } /* Check if given physical address is a breakpoint (according to current mapping) */ static TilemDebugBreakpoint *find_bp_physical(TilemDebugger *dbg, TilemCalc *calc, dword addr) { GSList *l; TilemDebugBreakpoint *bp; dword la, pa; int i, mapped[4]; /* NOTE: this assumes that bits 0-13 are unaffected by the mapping! This is true for all current models, but might need to be changed in the future */ for (i = 0; i < 4; i++) { la = (i << 14) + (addr & 0x3fff); pa = (*calc->hw.mem_ltop)(calc, la); mapped[i] = (addr == pa); } for (l = dbg->breakpoints; l; l = l->next) { bp = l->data; if (!(bp->mode & TILEM_DB_BREAK_EXEC)) continue; if (bp->type == TILEM_DB_BREAK_PHYSICAL && bp->start <= addr && bp->end >= addr && !bp->disabled) return bp; if (bp->type == TILEM_DB_BREAK_LOGICAL && !bp->disabled) { for (i = 0; i < 4; i++) { la = (i << 14) + (addr & 0x3fff); if (bp->start <= la && bp->end >= la && mapped[i]) return bp; } } } return NULL; } /* Check if line has a breakpoint set */ static TilemDebugBreakpoint *find_line_bp(TilemDisasmView *dv, dword pos) { TilemDebugBreakpoint *bp; dword addr = POS_TO_ADDR(pos); TilemCalc *calc; tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; if (dv->use_logical) bp = find_bp_logical(dv->dbg, calc, addr); else bp = find_bp_physical(dv->dbg, calc, addr); tilem_calc_emulator_unlock(dv->dbg->emu); return bp; } /* Enable breakpoint on the given line */ static void enable_line_bp(TilemDisasmView *dv, dword pos) { TilemDebugBreakpoint *bp, tmpbp; if ((bp = find_line_bp(dv, pos))) return; tmpbp.type = (dv->use_logical ? TILEM_DB_BREAK_LOGICAL : TILEM_DB_BREAK_PHYSICAL); tmpbp.mode = TILEM_DB_BREAK_EXEC; tmpbp.start = POS_TO_ADDR(pos); tmpbp.end = POS_TO_ADDR(pos); tmpbp.mask = (dv->use_logical ? 0xffff : 0xffffffff); tmpbp.disabled = 0; tilem_debugger_add_breakpoint(dv->dbg, &tmpbp); } /* Disable breakpoint on the given line */ static void disable_line_bp(TilemDisasmView *dv, dword pos) { TilemDebugBreakpoint *bp, tmpbp; if (!(bp = find_line_bp(dv, pos))) return; if (bp->mode != TILEM_DB_BREAK_EXEC || bp->start != bp->end) { /* special breakpoint; do not delete it, just disable it */ tmpbp = *bp; tmpbp.disabled = 1; tilem_debugger_change_breakpoint(dv->dbg, bp, &tmpbp); } else { /* regular breakpoint */ tilem_debugger_remove_breakpoint(dv->dbg, bp); } } /* Append a line to the dasm model */ static void append_dasm_line(TilemDisasmView *dv, TilemCalc *calc, GtkTreeModel *model, GtkTreeIter *iter, dword pos, dword *nextpos) { GtkTreeIter iter1; char *astr, *mnem, *args; dword addr, pc; gboolean ispc; TilemDebugBreakpoint *bp; GdkPixbuf *icon; g_return_if_fail(calc != NULL); gtk_list_store_append(GTK_LIST_STORE(model), &iter1); addr = POS_TO_ADDR(pos); astr = tilem_format_addr(dv->dbg, addr, !dv->use_logical); disassemble(dv, calc, pos, nextpos, &mnem, &args); if (!mnem) bp = NULL; else if (dv->use_logical) bp = find_bp_logical(dv->dbg, calc, addr); else bp = find_bp_physical(dv->dbg, calc, addr); if (!mnem || !dv->dbg->emu->paused) { ispc = FALSE; } else { pc = calc->z80.r.pc.w.l; if (!dv->use_logical) pc = (*calc->hw.mem_ltop)(calc, pc); ispc = (addr == pc); } icon = get_icon(dv, ispc, (bp != NULL)); gtk_list_store_set(GTK_LIST_STORE(model), &iter1, COL_POSITION, (int) pos, COL_ADDRESS, astr, COL_MNEMONIC, mnem, COL_SHOW_MNEMONIC, (mnem ? TRUE : FALSE), COL_ARGUMENTS, args, COL_ICON, icon, -1); if (icon) g_object_unref(icon); g_free(astr); g_free(mnem); g_free(args); if (iter) *iter = iter1; } /* Refresh the view by creating and populating a new model */ static void refresh_disassembly(TilemDisasmView *dv, dword pos, int nlines, dword selectpos) { GtkTreeModel *model; GtkTreeIter iter; GtkTreePath *selectpath = NULL; TilemCalc *calc; dword nextpos; int i; model = new_dasm_model(); dv->startpos = pos; tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; if (!calc) nlines = 0; for (i = 0; i < nlines; i++) { append_dasm_line(dv, calc, model, &iter, pos, &nextpos); if (pos == selectpos) selectpath = gtk_tree_model_get_path(model, &iter); pos = nextpos; } tilem_calc_emulator_unlock(dv->dbg->emu); dv->endpos = pos; dv->nlines = nlines; gtk_tree_view_set_model(GTK_TREE_VIEW(dv), model); g_object_unref(model); if (selectpath) { gtk_tree_view_set_cursor(GTK_TREE_VIEW(dv), selectpath, NULL, FALSE); gtk_tree_path_free(selectpath); } } /* Determine the (absolute) position and (display-relative) line number of the cursor, if any */ static gboolean get_cursor_line(TilemDisasmView *dv, dword *pos, int *linenum) { GtkTreePath *path; GtkTreeModel *model; GtkTreeIter iter; gint *i, p; gtk_tree_view_get_cursor(GTK_TREE_VIEW(dv), &path, NULL); if (!path) { if (pos) *pos = (dword) -1; if (linenum) *linenum = -1; return FALSE; } if (pos) { model = gtk_tree_view_get_model(GTK_TREE_VIEW(dv)); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get(model, &iter, COL_POSITION, &p, -1); *pos = p; } else { *pos = (dword) -1; } } if (linenum) { i = gtk_tree_path_get_indices(path); *linenum = i[0]; } gtk_tree_path_free(path); return TRUE; } /* Size allocation */ /* Get the desired height for the tree view (based on size of the data we've inserted into the model) */ static int get_parent_request_height(GtkWidget *w) { GtkRequisition req; (*GTK_WIDGET_CLASS(parent_class)->size_request)(w, &req); return req.height; } /* Widget is assigned a size and position */ static void tilem_disasm_view_size_allocate(GtkWidget *w, GtkAllocation *alloc) { TilemDisasmView *dv; GtkTreeModel *model; dword curpos; int n, height1, height2; g_return_if_fail(TILEM_IS_DISASM_VIEW(w)); dv = TILEM_DISASM_VIEW(w); (*GTK_WIDGET_CLASS(parent_class)->size_allocate)(w, alloc); if (alloc->height < 1) return; get_cursor_line(dv, &curpos, NULL); /* Calculate line height */ if (!dv->line_height) { model = new_dasm_model(); append_dummy_line(dv, model, NULL); gtk_tree_view_set_model(GTK_TREE_VIEW(dv), model); height1 = get_parent_request_height(w); append_dummy_line(dv, model, NULL); height2 = get_parent_request_height(w); dv->line_height = height2 - height1; dv->base_height = height1 - dv->line_height; g_object_unref(model); dv->nlines = 0; if (dv->line_height <= 0) { dv->line_height = 0; return; } } n = (alloc->height - dv->base_height) / dv->line_height; if (n < 1) n = 1; if (n != dv->nlines) refresh_disassembly(dv, dv->startpos, n, curpos); } /* Get widget's desired size */ static void tilem_disasm_view_size_request(GtkWidget *w, GtkRequisition *req) { (*GTK_WIDGET_CLASS(parent_class)->size_request)(w, req); req->height = 100; /* ignore requested height */ } /* Widget style set */ static void tilem_disasm_view_style_set(GtkWidget *w, GtkStyle *oldstyle) { TilemDisasmView *dv; GtkTreeModel *model; GtkTreeIter iter; GList *cols, *cp; GtkTreeViewColumn *col; int width; g_return_if_fail(TILEM_IS_DISASM_VIEW(w)); dv = TILEM_DISASM_VIEW(w); (*GTK_WIDGET_CLASS(parent_class)->style_set)(w, oldstyle); /* line height must be recalculated */ dv->line_height = 0; /* set column widths based on a dummy model */ model = new_dasm_model(); append_dummy_line(dv, model, &iter); cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(dv)); for (cp = cols; cp; cp = cp->next) { col = cp->data; gtk_tree_view_column_cell_set_cell_data(col, model, &iter, FALSE, FALSE); gtk_tree_view_column_cell_get_size(col, NULL, NULL, NULL, &width, NULL); gtk_tree_view_column_set_fixed_width(col, width + 2); } g_list_free(cols); g_object_unref(model); } /* Cursor movement commands */ /* Move up by COUNT lines */ static gboolean move_up_lines(TilemDisasmView *dv, int count) { TilemCalc *calc; dword pos; int linenum; if (!get_cursor_line(dv, NULL, &linenum)) linenum = 0; if (linenum >= count) return FALSE; tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; pos = dv->startpos; count -= linenum; while (count > 0) { pos = get_prev_pos(dv, calc, pos); count--; } tilem_calc_emulator_unlock(dv->dbg->emu); refresh_disassembly(dv, pos, dv->nlines, pos); return TRUE; } /* Move down by COUNT lines */ static gboolean move_down_lines(TilemDisasmView *dv, int count) { TilemCalc *calc; dword startpos, selpos; int linenum; if (!get_cursor_line(dv, NULL, &linenum)) linenum = -1; if (linenum + count < dv->nlines) return FALSE; tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; startpos = get_next_pos(dv, calc, dv->startpos); selpos = dv->endpos; count -= dv->nlines - linenum; while (count > 0) { startpos = get_next_pos(dv, calc, startpos); selpos = get_next_pos(dv, calc, selpos); count--; } tilem_calc_emulator_unlock(dv->dbg->emu); refresh_disassembly(dv, startpos, dv->nlines, selpos); return TRUE; } /* Move down by COUNT bytes */ static void move_bytes(TilemDisasmView *dv, int count) { dword pos, addr; const TilemCalc *calc = dv->dbg->emu->calc; g_return_if_fail(calc != NULL); if (!get_cursor_line(dv, &pos, NULL)) pos = dv->startpos; addr = POS_TO_ADDR(pos); if (dv->use_logical) addr = (addr + count) & 0xffff; else { addr += calc->hw.romsize + calc->hw.ramsize + count; addr %= calc->hw.romsize + calc->hw.ramsize; } pos = ADDR_TO_POS(addr); refresh_disassembly(dv, pos, dv->nlines, pos - 1); } /* Move the cursor (action signal) */ static gboolean tilem_disasm_view_move_cursor(GtkTreeView *tv, GtkMovementStep step, gint count) { TilemDisasmView *dv; g_return_val_if_fail(TILEM_IS_DISASM_VIEW(tv), FALSE); dv = TILEM_DISASM_VIEW(tv); if (!dv->dbg->emu->calc) return FALSE; switch (step) { case GTK_MOVEMENT_DISPLAY_LINES: if (count < 0) { if (move_up_lines(dv, -count)) return TRUE; } else { if (move_down_lines(dv, count)) return TRUE; } break; case GTK_MOVEMENT_PARAGRAPHS: case GTK_MOVEMENT_PARAGRAPH_ENDS: case GTK_MOVEMENT_PAGES: /* FIXME: might be better to move by actual "pages" of code */ move_bytes(dv, count * 0x100); return TRUE; case GTK_MOVEMENT_BUFFER_ENDS: move_bytes(dv, count * 0x4000); return TRUE; case GTK_MOVEMENT_LOGICAL_POSITIONS: case GTK_MOVEMENT_VISUAL_POSITIONS: case GTK_MOVEMENT_WORDS: case GTK_MOVEMENT_DISPLAY_LINE_ENDS: case GTK_MOVEMENT_HORIZONTAL_PAGES: default: break; } return (*GTK_TREE_VIEW_CLASS(parent_class)->move_cursor)(tv, step, count); } /* Popup menu */ static void toggle_bp(G_GNUC_UNUSED GtkCheckMenuItem *item, gpointer data) { TilemDisasmView *dv = data; tilem_disasm_view_toggle_breakpoint(dv); } void tilem_disasm_view_toggle_breakpoint(TilemDisasmView *dv) { dword curpos; g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); get_cursor_line(dv, &curpos, NULL); if (curpos == (dword) -1) return; if (find_line_bp(dv, curpos)) disable_line_bp(dv, curpos); else enable_line_bp(dv, curpos); } static void prompt_go_to(G_GNUC_UNUSED GtkMenuItem *item, gpointer data) { TilemDisasmView *dv = data; GtkWidget *window; dword curpos, addr; window = gtk_widget_get_toplevel(GTK_WIDGET(dv)); get_cursor_line(dv, &curpos, NULL); addr = POS_TO_ADDR(curpos); if (tilem_prompt_address(dv->dbg, GTK_WINDOW(window), "Go to Address", "Address:", &addr, !dv->use_logical, (curpos != (dword) -1))) tilem_disasm_view_go_to_address(dv, addr, dv->use_logical); } static void go_to_pc(G_GNUC_UNUSED GtkMenuItem *item, gpointer data) { TilemDisasmView *dv = data; TilemCalc *calc; dword pc; g_return_if_fail(dv->dbg != NULL); g_return_if_fail(dv->dbg->emu != NULL); g_return_if_fail(dv->dbg->emu->calc != NULL); tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; pc = calc->z80.r.pc.w.l; tilem_calc_emulator_unlock(dv->dbg->emu); tilem_disasm_view_go_to_address(dv, pc, TRUE); } /* Determine where to pop up menu (if not activated by a mouse event) */ static void place_menu(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) { TilemDisasmView *dv = data; GtkTreePath *path; GdkRectangle rect; GdkWindow *win; GdkScreen *screen; int n; win = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(dv)); gdk_window_get_origin(win, x, y); gtk_tree_view_get_cursor(GTK_TREE_VIEW(dv), &path, NULL); if (path) { gtk_tree_view_get_cell_area(GTK_TREE_VIEW(dv), path, NULL, &rect); gtk_tree_path_free(path); *y += rect.y + rect.height; } screen = gdk_drawable_get_screen(win); n = gdk_screen_get_monitor_at_point(screen, *x, *y); gtk_menu_set_monitor(menu, n); *push_in = FALSE; } /* Create and show the popup menu */ static void show_popup_menu(TilemDisasmView *dv, GdkEventButton *event) { GtkWidget *menu, *item; dword curpos; if (dv->popup_menu) gtk_widget_destroy(dv->popup_menu); dv->popup_menu = menu = gtk_menu_new(); /* Enable/disable breakpoint */ item = gtk_check_menu_item_new_with_mnemonic("_Breakpoint Here"); get_cursor_line(dv, &curpos, NULL); if (curpos == (dword) -1) gtk_widget_set_sensitive(item, FALSE); else if (find_line_bp(dv, curpos)) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); g_signal_connect(item, "toggled", G_CALLBACK(toggle_bp), dv); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); item = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); /* Jump to address */ item = gtk_menu_item_new_with_mnemonic("_Go to Address..."); g_signal_connect(item, "activate", G_CALLBACK(prompt_go_to), dv); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); item = gtk_menu_item_new_with_mnemonic("Go to P_C"); g_signal_connect(item, "activate", G_CALLBACK(go_to_pc), dv); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); if (event) gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time); else gtk_menu_popup(GTK_MENU(menu), NULL, NULL, &place_menu, dv, 0, gtk_get_current_event_time()); } /* Button pressed */ static gboolean tilem_disasm_view_button_press(GtkWidget *w, GdkEventButton *event) { g_return_val_if_fail(TILEM_IS_DISASM_VIEW(w), FALSE); (*GTK_WIDGET_CLASS(parent_class)->button_press_event)(w, event); if (event->button == 3) show_popup_menu(TILEM_DISASM_VIEW(w), event); return TRUE; } /* Key pressed to activate context menu */ static gboolean tilem_disasm_view_popup_menu(GtkWidget *w) { g_return_val_if_fail(TILEM_IS_DISASM_VIEW(w), FALSE); show_popup_menu(TILEM_DISASM_VIEW(w), NULL); return TRUE; } /* Row activated (double-clicked) */ static void tilem_disasm_view_row_activated(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col) { TilemDisasmView *dv = TILEM_DISASM_VIEW(tv); GtkTreeModel *model; GtkTreeIter iter; gint pos; model = gtk_tree_view_get_model(tv); if (!gtk_tree_model_get_iter(model, &iter, path)) return; gtk_tree_model_get(model, &iter, COL_POSITION, &pos, -1); if (col == dv->icon_column) { if (find_line_bp(dv, pos)) disable_line_bp(dv, pos); else enable_line_bp(dv, pos); } } /* Unrealize widget */ static void tilem_disasm_view_unrealize(GtkWidget *w) { TilemDisasmView *dv = TILEM_DISASM_VIEW(w); if (dv->popup_menu) gtk_widget_destroy(dv->popup_menu); dv->popup_menu = NULL; (*GTK_WIDGET_CLASS(parent_class)->unrealize)(w); } /* Initialize a new TilemDisasmView */ static void tilem_disasm_view_init(TilemDisasmView *dv) { GtkTreeView *tv = GTK_TREE_VIEW(dv); GtkCellRenderer *cell; GtkTreeViewColumn *col; dv->use_logical = TRUE; gtk_tree_view_set_enable_search(tv, FALSE); gtk_tree_view_set_fixed_height_mode(tv, TRUE); gtk_tree_view_set_headers_visible(tv, FALSE); cell = gtk_cell_renderer_pixbuf_new(); col = gtk_tree_view_column_new_with_attributes(NULL, cell, "pixbuf", COL_ICON, NULL); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_append_column(tv, col); dv->icon_column = col; cell = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes("Addr", cell, "text", COL_ADDRESS, NULL); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_append_column(tv, col); col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Disassembly"); cell = gtk_cell_renderer_text_new(); g_object_set(cell, "xpad", 10, NULL); gtk_tree_view_column_pack_start(col, cell, FALSE); gtk_tree_view_column_set_attributes(col, cell, "text", COL_MNEMONIC, "visible", COL_SHOW_MNEMONIC, NULL); cell = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(col, cell, TRUE); gtk_tree_view_column_set_attributes(col, cell, "text", COL_ARGUMENTS, NULL); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_expand(col, TRUE); gtk_tree_view_append_column(tv, col); } static const char default_style[] = "style \"tilem-disasm-default\" { font_name = \"Monospace\" } " "widget \"*.TilemDisasmView\" style:application \"tilem-disasm-default\""; /* Initialize the TilemDisasmView class */ static void tilem_disasm_view_class_init(TilemDisasmViewClass *klass) { GtkTreeViewClass *tv_class = GTK_TREE_VIEW_CLASS(klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); gtk_rc_parse_string(default_style); parent_class = g_type_class_peek_parent(klass); widget_class->style_set = &tilem_disasm_view_style_set; widget_class->size_request = &tilem_disasm_view_size_request; widget_class->size_allocate = &tilem_disasm_view_size_allocate; widget_class->button_press_event = &tilem_disasm_view_button_press; widget_class->popup_menu = &tilem_disasm_view_popup_menu; widget_class->unrealize = &tilem_disasm_view_unrealize; tv_class->move_cursor = &tilem_disasm_view_move_cursor; tv_class->row_activated = &tilem_disasm_view_row_activated; } GtkWidget * tilem_disasm_view_new(TilemDebugger *dbg) { TilemDisasmView *dv; g_return_val_if_fail(dbg != NULL, NULL); dv = g_object_new(TILEM_TYPE_DISASM_VIEW, NULL); dv->dbg = dbg; return GTK_WIDGET(dv); } /* Select memory addressing mode. */ void tilem_disasm_view_set_logical(TilemDisasmView *dv, gboolean logical) { dword start, curpos; TilemCalc *calc; g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); g_return_if_fail(dv->dbg->emu->calc != NULL); get_cursor_line(dv, &curpos, NULL); if (logical && !dv->use_logical) { tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; curpos = pos_ptol(calc, curpos); start = pos_ptol(calc, dv->startpos); tilem_calc_emulator_unlock(dv->dbg->emu); dv->use_logical = TRUE; refresh_disassembly(dv, start, dv->nlines, curpos); } else if (!logical && dv->use_logical) { tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; curpos = pos_ltop(calc, curpos); start = pos_ltop(calc, dv->startpos); tilem_calc_emulator_unlock(dv->dbg->emu); dv->use_logical = FALSE; refresh_disassembly(dv, start, dv->nlines, curpos); } } /* Refresh contents of view. */ void tilem_disasm_view_refresh(TilemDisasmView *dv) { dword curpos; g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); get_cursor_line(dv, &curpos, NULL); refresh_disassembly(dv, dv->startpos, dv->nlines, curpos); } /* Find tree path for the given position */ static GtkTreePath *find_path_for_position(GtkTreeModel *model, int pos) { gint p; GtkTreeIter iter; if (!gtk_tree_model_get_iter_first(model, &iter)) return NULL; do { gtk_tree_model_get(model, &iter, COL_POSITION, &p, -1); if (p == pos) { return gtk_tree_model_get_path(model, &iter); } } while (gtk_tree_model_iter_next(model, &iter)); return NULL; } /* Highlight the specified address. */ void tilem_disasm_view_go_to_address(TilemDisasmView *dv, dword addr, gboolean logical) { dword pos; GtkTreeModel *model; GtkTreePath *path; TilemCalc *calc; g_return_if_fail(TILEM_IS_DISASM_VIEW(dv)); tilem_calc_emulator_lock(dv->dbg->emu); calc = dv->dbg->emu->calc; if (logical) { addr &= 0xffff; if (dv->use_logical) pos = ADDR_TO_POS(addr); else pos = pos_ltop(calc, ADDR_TO_POS(addr)); } else { if (dv->use_logical) { addr = (*calc->hw.mem_ptol)(calc, addr); if (addr == (dword) -1) { tilem_calc_emulator_unlock(dv->dbg->emu); return; } } pos = ADDR_TO_POS(addr); } tilem_calc_emulator_unlock(dv->dbg->emu); if (pos >= dv->startpos && pos < dv->endpos) { model = gtk_tree_view_get_model(GTK_TREE_VIEW(dv)); path = find_path_for_position(model, pos); if (path) { gtk_tree_view_set_cursor(GTK_TREE_VIEW(dv), path, NULL, FALSE); gtk_tree_path_free(path); return; } } refresh_disassembly(dv, pos, dv->nlines, pos); } /* Get currently selected address. */ gboolean tilem_disasm_view_get_cursor(TilemDisasmView *dv, dword *addr, gboolean *is_logical) { dword pos; g_return_val_if_fail(TILEM_IS_DISASM_VIEW(dv), FALSE); if (is_logical) *is_logical = dv->use_logical; if (!get_cursor_line(dv, &pos, NULL)) return FALSE; if (addr) *addr = POS_TO_ADDR(pos); return TRUE; }