1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2011-2012 Benjamin Moody
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <gtk/gtk.h>
27 #include <ticalcs.h>
28 #include <tilem.h>
29 #include <tilemdb.h>
30
31 #include "gui.h"
32 #include "disasmview.h"
33
34 G_DEFINE_TYPE(TilemDisasmView, tilem_disasm_view, GTK_TYPE_TREE_VIEW);
35
36 /*
37 This is a HORRIBLE kludge. Don't ever do anything like this. ;)
38
39 We want a widget that has the look and feel of a GtkTreeView. But
40 our "data model" isn't consistent with a GtkTreeModel, since it
41 changes depending on where we are.
42
43 This widget keeps track of how high each row will be once rendered,
44 and uses that to construct a GtkListStore with the appropriate
45 number of rows to fill the window. We also override the move-cursor
46 signal so that we can handle the boundaries.
47 */
48
49 /* Model columns */
50 enum {
51 COL_POSITION,
52 COL_ADDRESS,
53 COL_MNEMONIC,
54 COL_ARGUMENTS,
55 COL_SHOW_MNEMONIC,
56 COL_ICON,
57 NUM_COLUMNS
58 };
59
60 static GtkTreeViewClass *parent_class;
61
62 /* We define two "positions" for each actual address; the second is
63 used if there's a label to be displayed at that address. */
64
65 #define POS_TO_ADDR(x) ((x) >> 1)
66 #define ADDR_TO_POS(x) ((x) << 1)
67
68 /* Disassembly */
69
70 /* Convert physical to logical address; if address is not currently
71 mapped, use the bank-A address */
default_ptol(TilemCalc * calc,dword addr)72 static dword default_ptol(TilemCalc *calc, dword addr)
73 {
74 dword addr_l;
75
76 g_return_val_if_fail(calc != NULL, 0);
77
78 addr_l = (*calc->hw.mem_ptol)(calc, addr);
79 if (addr_l == 0xffffffff)
80 addr_l = (addr & 0x3fff) | 0x4000;
81
82 return addr_l;
83 }
84
85 /* Check for a label at the given address (physical or logical
86 depending on the mode of the DisasmView) */
get_label(TilemDisasmView * dv,TilemCalc * calc,dword addr)87 static const char *get_label(TilemDisasmView *dv, TilemCalc *calc,
88 dword addr)
89 {
90 g_return_val_if_fail(calc != NULL, NULL);
91 g_return_val_if_fail(dv->dbg->dasm != NULL, NULL);
92
93 if (!dv->use_logical)
94 addr = default_ptol(calc, addr);
95
96 return tilem_disasm_get_label_at_address(dv->dbg->dasm, addr);
97 }
98
99 /* Disassemble a line */
disassemble(TilemDisasmView * dv,TilemCalc * calc,dword pos,dword * nextpos,char ** mnemonic,char ** args)100 static void disassemble(TilemDisasmView *dv, TilemCalc *calc, dword pos,
101 dword *nextpos, char **mnemonic, char **args)
102 {
103 dword addr = POS_TO_ADDR(pos);
104 const char *lbl;
105 char buf[500], *p;
106
107 g_return_if_fail(calc != NULL);
108 g_return_if_fail(dv->dbg->dasm != NULL);
109
110 if (!(pos & 1) && (lbl = get_label(dv, calc, addr))) {
111 if (mnemonic) {
112 *mnemonic = NULL;
113 *args = g_strdup_printf("%s:", lbl);
114 }
115
116 if (nextpos)
117 *nextpos = pos + 1;
118 }
119 else if (mnemonic) {
120 tilem_disasm_disassemble(dv->dbg->dasm, calc,
121 !dv->use_logical, addr,
122 &addr, buf, sizeof(buf));
123
124 p = strchr(buf, '\t');
125 if (p) {
126 *mnemonic = g_strndup(buf, p - buf);
127 *args = g_strdup(p + 1);
128 }
129 else {
130 *mnemonic = g_strdup(buf);
131 *args = NULL;
132 }
133
134 if (nextpos)
135 *nextpos = ADDR_TO_POS(addr);
136 }
137 else {
138 tilem_disasm_disassemble(dv->dbg->dasm, calc,
139 !dv->use_logical, addr,
140 &addr, NULL, 0);
141 if (nextpos)
142 *nextpos = ADDR_TO_POS(addr);
143 }
144 }
145
146 /* Get "next" position */
get_next_pos(TilemDisasmView * dv,TilemCalc * calc,dword pos)147 static dword get_next_pos(TilemDisasmView *dv, TilemCalc *calc, dword pos)
148 {
149 disassemble(dv, calc, pos, &pos, NULL, NULL);
150 return pos;
151 }
152
153 /* Get "previous" position */
get_prev_pos(TilemDisasmView * dv,TilemCalc * calc,dword pos)154 static dword get_prev_pos(TilemDisasmView *dv, TilemCalc *calc, dword pos)
155 {
156 dword addr = POS_TO_ADDR(pos);
157
158 g_return_val_if_fail(calc != NULL, 0);
159
160 if (pos & 1) {
161 return pos - 1;
162 }
163 else {
164 if (addr > 0)
165 addr--;
166 else if (dv->use_logical)
167 addr = 0xffff;
168 else
169 addr = (calc->hw.romsize + calc->hw.ramsize - 1);
170
171 if (get_label(dv, calc, addr))
172 return ADDR_TO_POS(addr) + 1;
173 else
174 return ADDR_TO_POS(addr);
175 }
176 }
177
178 /* Convert physical to logical position */
pos_ptol(TilemCalc * calc,dword pos)179 static dword pos_ptol(TilemCalc *calc, dword pos)
180 {
181 dword addr;
182
183 g_return_val_if_fail(calc != NULL, 0);
184
185 if (pos == (dword) -1)
186 return pos;
187
188 addr = default_ptol(calc, POS_TO_ADDR(pos));
189 return ADDR_TO_POS(addr) + (pos & 1);
190 }
191
192 /* Convert logical to physical position */
pos_ltop(TilemCalc * calc,dword pos)193 static dword pos_ltop(TilemCalc *calc, dword pos)
194 {
195 dword addr;
196
197 g_return_val_if_fail(calc != NULL, 0);
198
199 if (pos == (dword) -1)
200 return pos;
201
202 addr = (*calc->hw.mem_ltop)(calc, POS_TO_ADDR(pos));
203 return ADDR_TO_POS(addr) + (pos & 1);
204 }
205
206 /* Icons */
207
get_icon(TilemDisasmView * dv,gboolean ispc,gboolean isbp)208 static GdkPixbuf *get_icon(TilemDisasmView *dv, gboolean ispc, gboolean isbp)
209 {
210 const char *name;
211
212 if (ispc && isbp)
213 name = "tilem-disasm-break-pc";
214 else if (isbp)
215 name = "tilem-disasm-break";
216 else if (ispc)
217 name = "tilem-disasm-pc";
218 else
219 return NULL;
220
221 return gtk_widget_render_icon(GTK_WIDGET(dv), name,
222 GTK_ICON_SIZE_MENU, NULL);
223 }
224
225 /* List model management */
226
227 /* Create a new list store for disassembly */
new_dasm_model()228 static GtkTreeModel * new_dasm_model()
229 {
230 GtkListStore *store;
231
232 g_assert(NUM_COLUMNS == 6);
233 store = gtk_list_store_new(6,
234 G_TYPE_INT,
235 G_TYPE_STRING,
236 G_TYPE_STRING,
237 G_TYPE_STRING,
238 G_TYPE_BOOLEAN,
239 GDK_TYPE_PIXBUF);
240
241 return GTK_TREE_MODEL(store);
242 }
243
244 /* Append dummy data to the model; used for sizing */
append_dummy_line(TilemDisasmView * dv,GtkTreeModel * model,GtkTreeIter * iter)245 static void append_dummy_line(TilemDisasmView *dv, GtkTreeModel *model,
246 GtkTreeIter *iter)
247 {
248 GtkTreeIter iter1;
249 GdkPixbuf *icon;
250
251 gtk_list_store_append(GTK_LIST_STORE(model), &iter1);
252
253 icon = get_icon(dv, TRUE, FALSE);
254
255 gtk_list_store_set(GTK_LIST_STORE(model), &iter1,
256 COL_ICON, icon,
257 COL_ADDRESS, "DD:DDDD",
258 COL_MNEMONIC, "ROM_CALL",
259 COL_ARGUMENTS, "_fnord",
260 COL_SHOW_MNEMONIC, TRUE,
261 -1);
262
263 if (icon)
264 g_object_unref(icon);
265
266 if (iter)
267 *iter = iter1;
268 }
269
270 /* Check if given logical address is a breakpoint (according to
271 current mapping) */
find_bp_logical(TilemDebugger * dbg,TilemCalc * calc,dword addr)272 static TilemDebugBreakpoint *find_bp_logical(TilemDebugger *dbg,
273 TilemCalc *calc,
274 dword addr)
275 {
276 GSList *l;
277 TilemDebugBreakpoint *bp;
278 dword pa = (*calc->hw.mem_ltop)(calc, addr);
279
280 for (l = dbg->breakpoints; l; l = l->next) {
281 bp = l->data;
282 if (!(bp->mode & TILEM_DB_BREAK_EXEC))
283 continue;
284
285 if (bp->type == TILEM_DB_BREAK_LOGICAL
286 && bp->start <= addr
287 && bp->end >= addr
288 && !bp->disabled)
289 return bp;
290
291 if (bp->type == TILEM_DB_BREAK_PHYSICAL
292 && bp->start <= pa
293 && bp->end >= pa
294 && !bp->disabled)
295 return bp;
296 }
297
298 return NULL;
299 }
300
301 /* Check if given physical address is a breakpoint (according to
302 current mapping) */
find_bp_physical(TilemDebugger * dbg,TilemCalc * calc,dword addr)303 static TilemDebugBreakpoint *find_bp_physical(TilemDebugger *dbg,
304 TilemCalc *calc,
305 dword addr)
306 {
307 GSList *l;
308 TilemDebugBreakpoint *bp;
309 dword la, pa;
310 int i, mapped[4];
311
312 /* NOTE: this assumes that bits 0-13 are unaffected by the
313 mapping! This is true for all current models, but might
314 need to be changed in the future */
315 for (i = 0; i < 4; i++) {
316 la = (i << 14) + (addr & 0x3fff);
317 pa = (*calc->hw.mem_ltop)(calc, la);
318 mapped[i] = (addr == pa);
319 }
320
321 for (l = dbg->breakpoints; l; l = l->next) {
322 bp = l->data;
323 if (!(bp->mode & TILEM_DB_BREAK_EXEC))
324 continue;
325
326 if (bp->type == TILEM_DB_BREAK_PHYSICAL
327 && bp->start <= addr
328 && bp->end >= addr
329 && !bp->disabled)
330 return bp;
331
332 if (bp->type == TILEM_DB_BREAK_LOGICAL
333 && !bp->disabled) {
334 for (i = 0; i < 4; i++) {
335 la = (i << 14) + (addr & 0x3fff);
336 if (bp->start <= la
337 && bp->end >= la
338 && mapped[i])
339 return bp;
340 }
341 }
342 }
343
344 return NULL;
345 }
346
347 /* Check if line has a breakpoint set */
find_line_bp(TilemDisasmView * dv,dword pos)348 static TilemDebugBreakpoint *find_line_bp(TilemDisasmView *dv, dword pos)
349 {
350 TilemDebugBreakpoint *bp;
351 dword addr = POS_TO_ADDR(pos);
352 TilemCalc *calc;
353
354 tilem_calc_emulator_lock(dv->dbg->emu);
355 calc = dv->dbg->emu->calc;
356
357 if (dv->use_logical)
358 bp = find_bp_logical(dv->dbg, calc, addr);
359 else
360 bp = find_bp_physical(dv->dbg, calc, addr);
361
362 tilem_calc_emulator_unlock(dv->dbg->emu);
363
364 return bp;
365 }
366
367 /* Enable breakpoint on the given line */
enable_line_bp(TilemDisasmView * dv,dword pos)368 static void enable_line_bp(TilemDisasmView *dv, dword pos)
369 {
370 TilemDebugBreakpoint *bp, tmpbp;
371
372 if ((bp = find_line_bp(dv, pos)))
373 return;
374
375 tmpbp.type = (dv->use_logical
376 ? TILEM_DB_BREAK_LOGICAL
377 : TILEM_DB_BREAK_PHYSICAL);
378 tmpbp.mode = TILEM_DB_BREAK_EXEC;
379 tmpbp.start = POS_TO_ADDR(pos);
380 tmpbp.end = POS_TO_ADDR(pos);
381 tmpbp.mask = (dv->use_logical ? 0xffff : 0xffffffff);
382 tmpbp.disabled = 0;
383 tilem_debugger_add_breakpoint(dv->dbg, &tmpbp);
384 }
385
386 /* Disable breakpoint on the given line */
disable_line_bp(TilemDisasmView * dv,dword pos)387 static void disable_line_bp(TilemDisasmView *dv, dword pos)
388 {
389 TilemDebugBreakpoint *bp, tmpbp;
390
391 if (!(bp = find_line_bp(dv, pos)))
392 return;
393
394 if (bp->mode != TILEM_DB_BREAK_EXEC || bp->start != bp->end) {
395 /* special breakpoint; do not delete it, just disable it */
396 tmpbp = *bp;
397 tmpbp.disabled = 1;
398 tilem_debugger_change_breakpoint(dv->dbg, bp, &tmpbp);
399 }
400 else {
401 /* regular breakpoint */
402 tilem_debugger_remove_breakpoint(dv->dbg, bp);
403 }
404 }
405
406 /* Append a line to the dasm model */
append_dasm_line(TilemDisasmView * dv,TilemCalc * calc,GtkTreeModel * model,GtkTreeIter * iter,dword pos,dword * nextpos)407 static void append_dasm_line(TilemDisasmView *dv, TilemCalc *calc,
408 GtkTreeModel *model, GtkTreeIter *iter,
409 dword pos, dword *nextpos)
410 {
411 GtkTreeIter iter1;
412 char *astr, *mnem, *args;
413 dword addr, pc;
414 gboolean ispc;
415 TilemDebugBreakpoint *bp;
416 GdkPixbuf *icon;
417
418 g_return_if_fail(calc != NULL);
419
420 gtk_list_store_append(GTK_LIST_STORE(model), &iter1);
421
422 addr = POS_TO_ADDR(pos);
423 astr = tilem_format_addr(dv->dbg, addr, !dv->use_logical);
424
425 disassemble(dv, calc, pos, nextpos, &mnem, &args);
426
427 if (!mnem)
428 bp = NULL;
429 else if (dv->use_logical)
430 bp = find_bp_logical(dv->dbg, calc, addr);
431 else
432 bp = find_bp_physical(dv->dbg, calc, addr);
433
434 if (!mnem || !dv->dbg->emu->paused) {
435 ispc = FALSE;
436 }
437 else {
438 pc = calc->z80.r.pc.w.l;
439 if (!dv->use_logical)
440 pc = (*calc->hw.mem_ltop)(calc, pc);
441 ispc = (addr == pc);
442 }
443
444 icon = get_icon(dv, ispc, (bp != NULL));
445
446 gtk_list_store_set(GTK_LIST_STORE(model), &iter1,
447 COL_POSITION, (int) pos,
448 COL_ADDRESS, astr,
449 COL_MNEMONIC, mnem,
450 COL_SHOW_MNEMONIC, (mnem ? TRUE : FALSE),
451 COL_ARGUMENTS, args,
452 COL_ICON, icon,
453 -1);
454
455 if (icon)
456 g_object_unref(icon);
457
458 g_free(astr);
459 g_free(mnem);
460 g_free(args);
461
462 if (iter)
463 *iter = iter1;
464 }
465
466 /* Refresh the view by creating and populating a new model */
refresh_disassembly(TilemDisasmView * dv,dword pos,int nlines,dword selectpos)467 static void refresh_disassembly(TilemDisasmView *dv, dword pos, int nlines,
468 dword selectpos)
469 {
470 GtkTreeModel *model;
471 GtkTreeIter iter;
472 GtkTreePath *selectpath = NULL;
473 TilemCalc *calc;
474 dword nextpos;
475 int i;
476
477 model = new_dasm_model();
478
479 dv->startpos = pos;
480
481 tilem_calc_emulator_lock(dv->dbg->emu);
482 calc = dv->dbg->emu->calc;
483
484 if (!calc)
485 nlines = 0;
486
487 for (i = 0; i < nlines; i++) {
488 append_dasm_line(dv, calc, model, &iter, pos, &nextpos);
489
490 if (pos == selectpos)
491 selectpath = gtk_tree_model_get_path(model, &iter);
492
493 pos = nextpos;
494 }
495
496 tilem_calc_emulator_unlock(dv->dbg->emu);
497
498 dv->endpos = pos;
499 dv->nlines = nlines;
500
501 gtk_tree_view_set_model(GTK_TREE_VIEW(dv), model);
502 g_object_unref(model);
503
504 if (selectpath) {
505 gtk_tree_view_set_cursor(GTK_TREE_VIEW(dv), selectpath,
506 NULL, FALSE);
507 gtk_tree_path_free(selectpath);
508 }
509 }
510
511 /* Determine the (absolute) position and (display-relative) line
512 number of the cursor, if any */
get_cursor_line(TilemDisasmView * dv,dword * pos,int * linenum)513 static gboolean get_cursor_line(TilemDisasmView *dv, dword *pos,
514 int *linenum)
515 {
516 GtkTreePath *path;
517 GtkTreeModel *model;
518 GtkTreeIter iter;
519 gint *i, p;
520
521 gtk_tree_view_get_cursor(GTK_TREE_VIEW(dv), &path, NULL);
522 if (!path) {
523 if (pos) *pos = (dword) -1;
524 if (linenum) *linenum = -1;
525 return FALSE;
526 }
527
528 if (pos) {
529 model = gtk_tree_view_get_model(GTK_TREE_VIEW(dv));
530 if (gtk_tree_model_get_iter(model, &iter, path)) {
531 gtk_tree_model_get(model, &iter,
532 COL_POSITION, &p, -1);
533 *pos = p;
534 }
535 else {
536 *pos = (dword) -1;
537 }
538 }
539
540 if (linenum) {
541 i = gtk_tree_path_get_indices(path);
542 *linenum = i[0];
543 }
544
545 gtk_tree_path_free(path);
546
547 return TRUE;
548 }
549
550 /* Size allocation */
551
552 /* Get the desired height for the tree view (based on size of the data
553 we've inserted into the model) */
get_parent_request_height(GtkWidget * w)554 static int get_parent_request_height(GtkWidget *w)
555 {
556 GtkRequisition req;
557 (*GTK_WIDGET_CLASS(parent_class)->size_request)(w, &req);
558 return req.height;
559 }
560
561 /* Widget is assigned a size and position */
tilem_disasm_view_size_allocate(GtkWidget * w,GtkAllocation * alloc)562 static void tilem_disasm_view_size_allocate(GtkWidget *w,
563 GtkAllocation *alloc)
564 {
565 TilemDisasmView *dv;
566 GtkTreeModel *model;
567 dword curpos;
568 int n, height1, height2;
569
570 g_return_if_fail(TILEM_IS_DISASM_VIEW(w));
571 dv = TILEM_DISASM_VIEW(w);
572
573 (*GTK_WIDGET_CLASS(parent_class)->size_allocate)(w, alloc);
574
575 if (alloc->height < 1)
576 return;
577
578 get_cursor_line(dv, &curpos, NULL);
579
580 /* Calculate line height */
581 if (!dv->line_height) {
582 model = new_dasm_model();
583
584 append_dummy_line(dv, model, NULL);
585 gtk_tree_view_set_model(GTK_TREE_VIEW(dv), model);
586 height1 = get_parent_request_height(w);
587
588 append_dummy_line(dv, model, NULL);
589 height2 = get_parent_request_height(w);
590
591 dv->line_height = height2 - height1;
592 dv->base_height = height1 - dv->line_height;
593
594 g_object_unref(model);
595
596 dv->nlines = 0;
597
598 if (dv->line_height <= 0) {
599 dv->line_height = 0;
600 return;
601 }
602 }
603
604 n = (alloc->height - dv->base_height) / dv->line_height;
605
606 if (n < 1)
607 n = 1;
608
609 if (n != dv->nlines)
610 refresh_disassembly(dv, dv->startpos, n, curpos);
611 }
612
613 /* Get widget's desired size */
tilem_disasm_view_size_request(GtkWidget * w,GtkRequisition * req)614 static void tilem_disasm_view_size_request(GtkWidget *w, GtkRequisition *req)
615 {
616 (*GTK_WIDGET_CLASS(parent_class)->size_request)(w, req);
617 req->height = 100; /* ignore requested height */
618 }
619
620 /* Widget style set */
tilem_disasm_view_style_set(GtkWidget * w,GtkStyle * oldstyle)621 static void tilem_disasm_view_style_set(GtkWidget *w, GtkStyle *oldstyle)
622 {
623 TilemDisasmView *dv;
624 GtkTreeModel *model;
625 GtkTreeIter iter;
626 GList *cols, *cp;
627 GtkTreeViewColumn *col;
628 int width;
629
630 g_return_if_fail(TILEM_IS_DISASM_VIEW(w));
631 dv = TILEM_DISASM_VIEW(w);
632
633 (*GTK_WIDGET_CLASS(parent_class)->style_set)(w, oldstyle);
634
635 /* line height must be recalculated */
636 dv->line_height = 0;
637
638 /* set column widths based on a dummy model */
639
640 model = new_dasm_model();
641 append_dummy_line(dv, model, &iter);
642
643 cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(dv));
644 for (cp = cols; cp; cp = cp->next) {
645 col = cp->data;
646 gtk_tree_view_column_cell_set_cell_data(col, model, &iter,
647 FALSE, FALSE);
648 gtk_tree_view_column_cell_get_size(col, NULL, NULL, NULL,
649 &width, NULL);
650 gtk_tree_view_column_set_fixed_width(col, width + 2);
651 }
652 g_list_free(cols);
653
654 g_object_unref(model);
655 }
656
657 /* Cursor movement commands */
658
659 /* Move up by COUNT lines */
move_up_lines(TilemDisasmView * dv,int count)660 static gboolean move_up_lines(TilemDisasmView *dv, int count)
661 {
662 TilemCalc *calc;
663 dword pos;
664 int linenum;
665
666 if (!get_cursor_line(dv, NULL, &linenum))
667 linenum = 0;
668
669 if (linenum >= count)
670 return FALSE;
671
672 tilem_calc_emulator_lock(dv->dbg->emu);
673 calc = dv->dbg->emu->calc;
674
675 pos = dv->startpos;
676 count -= linenum;
677 while (count > 0) {
678 pos = get_prev_pos(dv, calc, pos);
679 count--;
680 }
681
682 tilem_calc_emulator_unlock(dv->dbg->emu);
683
684 refresh_disassembly(dv, pos, dv->nlines, pos);
685
686 return TRUE;
687 }
688
689 /* Move down by COUNT lines */
move_down_lines(TilemDisasmView * dv,int count)690 static gboolean move_down_lines(TilemDisasmView *dv, int count)
691 {
692 TilemCalc *calc;
693 dword startpos, selpos;
694 int linenum;
695
696 if (!get_cursor_line(dv, NULL, &linenum))
697 linenum = -1;
698
699 if (linenum + count < dv->nlines)
700 return FALSE;
701
702 tilem_calc_emulator_lock(dv->dbg->emu);
703 calc = dv->dbg->emu->calc;
704
705 startpos = get_next_pos(dv, calc, dv->startpos);
706 selpos = dv->endpos;
707 count -= dv->nlines - linenum;
708
709 while (count > 0) {
710 startpos = get_next_pos(dv, calc, startpos);
711 selpos = get_next_pos(dv, calc, selpos);
712 count--;
713 }
714
715 tilem_calc_emulator_unlock(dv->dbg->emu);
716
717 refresh_disassembly(dv, startpos, dv->nlines, selpos);
718
719 return TRUE;
720 }
721
722 /* Move down by COUNT bytes */
move_bytes(TilemDisasmView * dv,int count)723 static void move_bytes(TilemDisasmView *dv, int count)
724 {
725 dword pos, addr;
726 const TilemCalc *calc = dv->dbg->emu->calc;
727
728 g_return_if_fail(calc != NULL);
729
730 if (!get_cursor_line(dv, &pos, NULL))
731 pos = dv->startpos;
732
733 addr = POS_TO_ADDR(pos);
734
735 if (dv->use_logical)
736 addr = (addr + count) & 0xffff;
737 else {
738 addr += calc->hw.romsize + calc->hw.ramsize + count;
739 addr %= calc->hw.romsize + calc->hw.ramsize;
740 }
741
742 pos = ADDR_TO_POS(addr);
743 refresh_disassembly(dv, pos, dv->nlines, pos - 1);
744 }
745
746 /* Move the cursor (action signal) */
tilem_disasm_view_move_cursor(GtkTreeView * tv,GtkMovementStep step,gint count)747 static gboolean tilem_disasm_view_move_cursor(GtkTreeView *tv,
748 GtkMovementStep step,
749 gint count)
750 {
751 TilemDisasmView *dv;
752
753 g_return_val_if_fail(TILEM_IS_DISASM_VIEW(tv), FALSE);
754 dv = TILEM_DISASM_VIEW(tv);
755
756 if (!dv->dbg->emu->calc)
757 return FALSE;
758
759 switch (step) {
760 case GTK_MOVEMENT_DISPLAY_LINES:
761 if (count < 0) {
762 if (move_up_lines(dv, -count))
763 return TRUE;
764 }
765 else {
766 if (move_down_lines(dv, count))
767 return TRUE;
768 }
769 break;
770
771 case GTK_MOVEMENT_PARAGRAPHS:
772 case GTK_MOVEMENT_PARAGRAPH_ENDS:
773 case GTK_MOVEMENT_PAGES:
774 /* FIXME: might be better to move by actual "pages" of code */
775 move_bytes(dv, count * 0x100);
776 return TRUE;
777
778 case GTK_MOVEMENT_BUFFER_ENDS:
779 move_bytes(dv, count * 0x4000);
780 return TRUE;
781
782 case GTK_MOVEMENT_LOGICAL_POSITIONS:
783 case GTK_MOVEMENT_VISUAL_POSITIONS:
784 case GTK_MOVEMENT_WORDS:
785 case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
786 case GTK_MOVEMENT_HORIZONTAL_PAGES:
787 default:
788 break;
789 }
790
791 return (*GTK_TREE_VIEW_CLASS(parent_class)->move_cursor)(tv, step, count);
792 }
793
794 /* Popup menu */
795
toggle_bp(G_GNUC_UNUSED GtkCheckMenuItem * item,gpointer data)796 static void toggle_bp(G_GNUC_UNUSED GtkCheckMenuItem *item, gpointer data)
797 {
798 TilemDisasmView *dv = data;
799 tilem_disasm_view_toggle_breakpoint(dv);
800 }
801
tilem_disasm_view_toggle_breakpoint(TilemDisasmView * dv)802 void tilem_disasm_view_toggle_breakpoint(TilemDisasmView *dv)
803 {
804 dword curpos;
805
806 g_return_if_fail(TILEM_IS_DISASM_VIEW(dv));
807
808 get_cursor_line(dv, &curpos, NULL);
809 if (curpos == (dword) -1)
810 return;
811
812 if (find_line_bp(dv, curpos))
813 disable_line_bp(dv, curpos);
814 else
815 enable_line_bp(dv, curpos);
816 }
817
prompt_go_to(G_GNUC_UNUSED GtkMenuItem * item,gpointer data)818 static void prompt_go_to(G_GNUC_UNUSED GtkMenuItem *item, gpointer data)
819 {
820 TilemDisasmView *dv = data;
821 GtkWidget *window;
822 dword curpos, addr;
823
824 window = gtk_widget_get_toplevel(GTK_WIDGET(dv));
825
826 get_cursor_line(dv, &curpos, NULL);
827 addr = POS_TO_ADDR(curpos);
828
829 if (tilem_prompt_address(dv->dbg, GTK_WINDOW(window),
830 "Go to Address", "Address:",
831 &addr, !dv->use_logical,
832 (curpos != (dword) -1)))
833 tilem_disasm_view_go_to_address(dv, addr, dv->use_logical);
834 }
835
go_to_pc(G_GNUC_UNUSED GtkMenuItem * item,gpointer data)836 static void go_to_pc(G_GNUC_UNUSED GtkMenuItem *item, gpointer data)
837 {
838 TilemDisasmView *dv = data;
839 TilemCalc *calc;
840 dword pc;
841
842 g_return_if_fail(dv->dbg != NULL);
843 g_return_if_fail(dv->dbg->emu != NULL);
844 g_return_if_fail(dv->dbg->emu->calc != NULL);
845
846 tilem_calc_emulator_lock(dv->dbg->emu);
847 calc = dv->dbg->emu->calc;
848 pc = calc->z80.r.pc.w.l;
849 tilem_calc_emulator_unlock(dv->dbg->emu);
850
851 tilem_disasm_view_go_to_address(dv, pc, TRUE);
852 }
853
854 /* Determine where to pop up menu (if not activated by a mouse event) */
place_menu(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer data)855 static void place_menu(GtkMenu *menu, gint *x, gint *y,
856 gboolean *push_in, gpointer data)
857 {
858 TilemDisasmView *dv = data;
859 GtkTreePath *path;
860 GdkRectangle rect;
861 GdkWindow *win;
862 GdkScreen *screen;
863 int n;
864
865 win = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(dv));
866 gdk_window_get_origin(win, x, y);
867
868 gtk_tree_view_get_cursor(GTK_TREE_VIEW(dv), &path, NULL);
869 if (path) {
870 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(dv), path, NULL, &rect);
871 gtk_tree_path_free(path);
872 *y += rect.y + rect.height;
873 }
874
875 screen = gdk_drawable_get_screen(win);
876 n = gdk_screen_get_monitor_at_point(screen, *x, *y);
877 gtk_menu_set_monitor(menu, n);
878
879 *push_in = FALSE;
880 }
881
882 /* Create and show the popup menu */
show_popup_menu(TilemDisasmView * dv,GdkEventButton * event)883 static void show_popup_menu(TilemDisasmView *dv, GdkEventButton *event)
884 {
885 GtkWidget *menu, *item;
886 dword curpos;
887
888 if (dv->popup_menu)
889 gtk_widget_destroy(dv->popup_menu);
890 dv->popup_menu = menu = gtk_menu_new();
891
892 /* Enable/disable breakpoint */
893
894 item = gtk_check_menu_item_new_with_mnemonic("_Breakpoint Here");
895
896 get_cursor_line(dv, &curpos, NULL);
897 if (curpos == (dword) -1)
898 gtk_widget_set_sensitive(item, FALSE);
899 else if (find_line_bp(dv, curpos))
900 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
901
902 g_signal_connect(item, "toggled",
903 G_CALLBACK(toggle_bp), dv);
904
905 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
906 gtk_widget_show(item);
907
908 item = gtk_separator_menu_item_new();
909 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
910 gtk_widget_show(item);
911
912 /* Jump to address */
913
914 item = gtk_menu_item_new_with_mnemonic("_Go to Address...");
915 g_signal_connect(item, "activate", G_CALLBACK(prompt_go_to), dv);
916 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
917 gtk_widget_show(item);
918
919 item = gtk_menu_item_new_with_mnemonic("Go to P_C");
920 g_signal_connect(item, "activate", G_CALLBACK(go_to_pc), dv);
921 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
922 gtk_widget_show(item);
923
924 if (event)
925 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
926 event->button, event->time);
927 else
928 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, &place_menu, dv,
929 0, gtk_get_current_event_time());
930 }
931
932 /* Button pressed */
tilem_disasm_view_button_press(GtkWidget * w,GdkEventButton * event)933 static gboolean tilem_disasm_view_button_press(GtkWidget *w,
934 GdkEventButton *event)
935 {
936 g_return_val_if_fail(TILEM_IS_DISASM_VIEW(w), FALSE);
937
938 (*GTK_WIDGET_CLASS(parent_class)->button_press_event)(w, event);
939
940 if (event->button == 3)
941 show_popup_menu(TILEM_DISASM_VIEW(w), event);
942
943 return TRUE;
944 }
945
946 /* Key pressed to activate context menu */
tilem_disasm_view_popup_menu(GtkWidget * w)947 static gboolean tilem_disasm_view_popup_menu(GtkWidget *w)
948 {
949 g_return_val_if_fail(TILEM_IS_DISASM_VIEW(w), FALSE);
950 show_popup_menu(TILEM_DISASM_VIEW(w), NULL);
951 return TRUE;
952 }
953
954 /* Row activated (double-clicked) */
tilem_disasm_view_row_activated(GtkTreeView * tv,GtkTreePath * path,GtkTreeViewColumn * col)955 static void tilem_disasm_view_row_activated(GtkTreeView *tv, GtkTreePath *path,
956 GtkTreeViewColumn *col)
957 {
958 TilemDisasmView *dv = TILEM_DISASM_VIEW(tv);
959 GtkTreeModel *model;
960 GtkTreeIter iter;
961 gint pos;
962
963 model = gtk_tree_view_get_model(tv);
964 if (!gtk_tree_model_get_iter(model, &iter, path))
965 return;
966
967 gtk_tree_model_get(model, &iter, COL_POSITION, &pos, -1);
968
969 if (col == dv->icon_column) {
970 if (find_line_bp(dv, pos))
971 disable_line_bp(dv, pos);
972 else
973 enable_line_bp(dv, pos);
974 }
975 }
976
977 /* Unrealize widget */
tilem_disasm_view_unrealize(GtkWidget * w)978 static void tilem_disasm_view_unrealize(GtkWidget *w)
979 {
980 TilemDisasmView *dv = TILEM_DISASM_VIEW(w);
981
982 if (dv->popup_menu)
983 gtk_widget_destroy(dv->popup_menu);
984 dv->popup_menu = NULL;
985
986 (*GTK_WIDGET_CLASS(parent_class)->unrealize)(w);
987 }
988
989 /* Initialize a new TilemDisasmView */
tilem_disasm_view_init(TilemDisasmView * dv)990 static void tilem_disasm_view_init(TilemDisasmView *dv)
991 {
992 GtkTreeView *tv = GTK_TREE_VIEW(dv);
993 GtkCellRenderer *cell;
994 GtkTreeViewColumn *col;
995
996 dv->use_logical = TRUE;
997
998 gtk_tree_view_set_enable_search(tv, FALSE);
999 gtk_tree_view_set_fixed_height_mode(tv, TRUE);
1000 gtk_tree_view_set_headers_visible(tv, FALSE);
1001
1002 cell = gtk_cell_renderer_pixbuf_new();
1003 col = gtk_tree_view_column_new_with_attributes(NULL, cell,
1004 "pixbuf", COL_ICON,
1005 NULL);
1006 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
1007 gtk_tree_view_append_column(tv, col);
1008 dv->icon_column = col;
1009
1010 cell = gtk_cell_renderer_text_new();
1011 col = gtk_tree_view_column_new_with_attributes("Addr", cell,
1012 "text", COL_ADDRESS,
1013 NULL);
1014 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
1015 gtk_tree_view_append_column(tv, col);
1016
1017 col = gtk_tree_view_column_new();
1018 gtk_tree_view_column_set_title(col, "Disassembly");
1019
1020 cell = gtk_cell_renderer_text_new();
1021 g_object_set(cell, "xpad", 10, NULL);
1022 gtk_tree_view_column_pack_start(col, cell, FALSE);
1023 gtk_tree_view_column_set_attributes(col, cell,
1024 "text", COL_MNEMONIC,
1025 "visible", COL_SHOW_MNEMONIC,
1026 NULL);
1027
1028 cell = gtk_cell_renderer_text_new();
1029 gtk_tree_view_column_pack_start(col, cell, TRUE);
1030 gtk_tree_view_column_set_attributes(col, cell,
1031 "text", COL_ARGUMENTS,
1032 NULL);
1033
1034 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
1035 gtk_tree_view_column_set_expand(col, TRUE);
1036 gtk_tree_view_append_column(tv, col);
1037 }
1038
1039 static const char default_style[] =
1040 "style \"tilem-disasm-default\" { font_name = \"Monospace\" } "
1041 "widget \"*.TilemDisasmView\" style:application \"tilem-disasm-default\"";
1042
1043 /* Initialize the TilemDisasmView class */
tilem_disasm_view_class_init(TilemDisasmViewClass * klass)1044 static void tilem_disasm_view_class_init(TilemDisasmViewClass *klass)
1045 {
1046 GtkTreeViewClass *tv_class = GTK_TREE_VIEW_CLASS(klass);
1047 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
1048
1049 gtk_rc_parse_string(default_style);
1050
1051 parent_class = g_type_class_peek_parent(klass);
1052
1053 widget_class->style_set = &tilem_disasm_view_style_set;
1054 widget_class->size_request = &tilem_disasm_view_size_request;
1055 widget_class->size_allocate = &tilem_disasm_view_size_allocate;
1056 widget_class->button_press_event = &tilem_disasm_view_button_press;
1057 widget_class->popup_menu = &tilem_disasm_view_popup_menu;
1058 widget_class->unrealize = &tilem_disasm_view_unrealize;
1059 tv_class->move_cursor = &tilem_disasm_view_move_cursor;
1060 tv_class->row_activated = &tilem_disasm_view_row_activated;
1061 }
1062
tilem_disasm_view_new(TilemDebugger * dbg)1063 GtkWidget * tilem_disasm_view_new(TilemDebugger *dbg)
1064 {
1065 TilemDisasmView *dv;
1066
1067 g_return_val_if_fail(dbg != NULL, NULL);
1068
1069 dv = g_object_new(TILEM_TYPE_DISASM_VIEW, NULL);
1070 dv->dbg = dbg;
1071
1072 return GTK_WIDGET(dv);
1073 }
1074
1075 /* Select memory addressing mode. */
tilem_disasm_view_set_logical(TilemDisasmView * dv,gboolean logical)1076 void tilem_disasm_view_set_logical(TilemDisasmView *dv, gboolean logical)
1077 {
1078 dword start, curpos;
1079 TilemCalc *calc;
1080
1081 g_return_if_fail(TILEM_IS_DISASM_VIEW(dv));
1082 g_return_if_fail(dv->dbg->emu->calc != NULL);
1083
1084 get_cursor_line(dv, &curpos, NULL);
1085
1086 if (logical && !dv->use_logical) {
1087 tilem_calc_emulator_lock(dv->dbg->emu);
1088 calc = dv->dbg->emu->calc;
1089 curpos = pos_ptol(calc, curpos);
1090 start = pos_ptol(calc, dv->startpos);
1091 tilem_calc_emulator_unlock(dv->dbg->emu);
1092
1093 dv->use_logical = TRUE;
1094 refresh_disassembly(dv, start, dv->nlines, curpos);
1095 }
1096 else if (!logical && dv->use_logical) {
1097 tilem_calc_emulator_lock(dv->dbg->emu);
1098 calc = dv->dbg->emu->calc;
1099 curpos = pos_ltop(calc, curpos);
1100 start = pos_ltop(calc, dv->startpos);
1101 tilem_calc_emulator_unlock(dv->dbg->emu);
1102
1103 dv->use_logical = FALSE;
1104 refresh_disassembly(dv, start, dv->nlines, curpos);
1105 }
1106 }
1107
1108 /* Refresh contents of view. */
tilem_disasm_view_refresh(TilemDisasmView * dv)1109 void tilem_disasm_view_refresh(TilemDisasmView *dv)
1110 {
1111 dword curpos;
1112 g_return_if_fail(TILEM_IS_DISASM_VIEW(dv));
1113 get_cursor_line(dv, &curpos, NULL);
1114 refresh_disassembly(dv, dv->startpos, dv->nlines, curpos);
1115 }
1116
1117 /* Find tree path for the given position */
find_path_for_position(GtkTreeModel * model,int pos)1118 static GtkTreePath *find_path_for_position(GtkTreeModel *model, int pos)
1119 {
1120 gint p;
1121 GtkTreeIter iter;
1122
1123 if (!gtk_tree_model_get_iter_first(model, &iter))
1124 return NULL;
1125
1126 do {
1127 gtk_tree_model_get(model, &iter, COL_POSITION, &p, -1);
1128 if (p == pos) {
1129 return gtk_tree_model_get_path(model, &iter);
1130 }
1131 } while (gtk_tree_model_iter_next(model, &iter));
1132
1133 return NULL;
1134 }
1135
1136 /* Highlight the specified address. */
tilem_disasm_view_go_to_address(TilemDisasmView * dv,dword addr,gboolean logical)1137 void tilem_disasm_view_go_to_address(TilemDisasmView *dv, dword addr,
1138 gboolean logical)
1139 {
1140 dword pos;
1141 GtkTreeModel *model;
1142 GtkTreePath *path;
1143 TilemCalc *calc;
1144
1145 g_return_if_fail(TILEM_IS_DISASM_VIEW(dv));
1146
1147 tilem_calc_emulator_lock(dv->dbg->emu);
1148 calc = dv->dbg->emu->calc;
1149
1150 if (logical) {
1151 addr &= 0xffff;
1152 if (dv->use_logical)
1153 pos = ADDR_TO_POS(addr);
1154 else
1155 pos = pos_ltop(calc, ADDR_TO_POS(addr));
1156 }
1157 else {
1158 if (dv->use_logical) {
1159 addr = (*calc->hw.mem_ptol)(calc, addr);
1160 if (addr == (dword) -1) {
1161 tilem_calc_emulator_unlock(dv->dbg->emu);
1162 return;
1163 }
1164 }
1165 pos = ADDR_TO_POS(addr);
1166 }
1167
1168 tilem_calc_emulator_unlock(dv->dbg->emu);
1169
1170 if (pos >= dv->startpos && pos < dv->endpos) {
1171 model = gtk_tree_view_get_model(GTK_TREE_VIEW(dv));
1172 path = find_path_for_position(model, pos);
1173 if (path) {
1174 gtk_tree_view_set_cursor(GTK_TREE_VIEW(dv), path,
1175 NULL, FALSE);
1176 gtk_tree_path_free(path);
1177 return;
1178 }
1179 }
1180
1181 refresh_disassembly(dv, pos, dv->nlines, pos);
1182 }
1183
1184 /* Get currently selected address. */
tilem_disasm_view_get_cursor(TilemDisasmView * dv,dword * addr,gboolean * is_logical)1185 gboolean tilem_disasm_view_get_cursor(TilemDisasmView *dv, dword *addr,
1186 gboolean *is_logical)
1187 {
1188 dword pos;
1189
1190 g_return_val_if_fail(TILEM_IS_DISASM_VIEW(dv), FALSE);
1191
1192 if (is_logical) *is_logical = dv->use_logical;
1193
1194 if (!get_cursor_line(dv, &pos, NULL))
1195 return FALSE;
1196
1197 if (addr) *addr = POS_TO_ADDR(pos);
1198 return TRUE;
1199 }
1200
1201