1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2011 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 <stdlib.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <ticalcs.h>
30 #include <tilem.h>
31 #include <tilemdb.h>
32
33 #include "gui.h"
34 #include "disasmview.h"
35 #include "fixedtreeview.h"
36
37 /**************** Add/edit breakpoint dialog ****************/
38
39 struct hex_entry {
40 GtkWidget *addr_label;
41 GtkWidget *addr_entry;
42 GtkWidget *page_label;
43 GtkWidget *page_entry;
44 };
45
46 struct breakpoint_dlg {
47 TilemDebugger *dbg;
48
49 GtkWidget *dlg;
50 GtkWidget *box;
51
52 GtkWidget *type_combo;
53 GtkWidget *access_cb[3];
54 GtkWidget *single_rb;
55 GtkWidget *range_rb;
56 GtkWidget *access_label;
57 GtkWidget *address_label;
58
59 struct hex_entry start;
60 struct hex_entry end;
61 };
62
63 static const struct {
64 char abbrev;
65 const char *desc;
66 const char *value_label;
67 int use_pages;
68 guint access_mask;
69 } type_info[] = {
70 { 'M', "Memory address (logical)", "Address", 0, 7 },
71 { 'M', "Memory address (absolute)", "Address", 1, 7 },
72 { 'P', "I/O port", "Port Number", 0, 6 },
73 { 'I', "Z80 instruction", "Opcode", 0, 0 }
74 };
75
76 /* Determine currently selected address type */
get_bp_type(struct breakpoint_dlg * bpdlg)77 static guint get_bp_type(struct breakpoint_dlg *bpdlg)
78 {
79 int i = gtk_combo_box_get_active(GTK_COMBO_BOX(bpdlg->type_combo));
80 return (i < 0 ? 0 : i);
81 }
82
83 /* Format address as a string */
hex_entry_set_value(struct hex_entry * he,TilemDebugger * dbg,int type,dword value)84 static void hex_entry_set_value(struct hex_entry *he, TilemDebugger *dbg,
85 int type, dword value)
86 {
87 const TilemCalc *calc;
88 char buf[20];
89 unsigned int page;
90
91 g_return_if_fail(dbg->emu != NULL);
92 g_return_if_fail(dbg->emu->calc != NULL);
93
94 calc = dbg->emu->calc;
95
96 switch (type) {
97 case TILEM_DB_BREAK_LOGICAL:
98 g_snprintf(buf, sizeof(buf), "%04X", value);
99 break;
100
101 case TILEM_DB_BREAK_PHYSICAL:
102 if (value >= calc->hw.romsize) {
103 value -= calc->hw.romsize;
104 page = (value >> 14) + calc->hw.rampagemask;
105 }
106 else {
107 page = (value >> 14);
108 }
109
110 g_snprintf(buf, sizeof(buf), "%02X", page);
111 gtk_entry_set_text(GTK_ENTRY(he->page_entry), buf);
112
113 g_snprintf(buf, sizeof(buf), "%04X", value & 0x3fff);
114 break;
115
116 case TILEM_DB_BREAK_PORT:
117 g_snprintf(buf, sizeof(buf), "%02X", value);
118 break;
119
120 case TILEM_DB_BREAK_OPCODE:
121 if (value < 0x100)
122 g_snprintf(buf, sizeof(buf), "%02X", value);
123 else if (value < 0x10000)
124 g_snprintf(buf, sizeof(buf), "%04X", value);
125 else if (value < 0x1000000)
126 g_snprintf(buf, sizeof(buf), "%06X", value);
127 else
128 g_snprintf(buf, sizeof(buf), "%08X", value);
129 break;
130
131 default:
132 g_return_if_reached();
133 }
134
135 gtk_entry_set_text(GTK_ENTRY(he->addr_entry), buf);
136 }
137
138 /* Parse contents of entry */
parse_num(TilemDebugger * dbg,const char * s,dword * a)139 static gboolean parse_num(TilemDebugger *dbg, const char *s, dword *a)
140 {
141 const char *n;
142 char *e;
143
144 g_return_val_if_fail(s != NULL, FALSE);
145
146 if (s[0] == '$')
147 n = s + 1;
148 else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
149 n = s + 2;
150 else
151 n = s;
152
153 *a = strtol(n, &e, 16);
154 if (e != n) {
155 if (*e == 'h' || *e == 'H')
156 e++;
157 if (*e == 0)
158 return TRUE;
159 }
160
161 if (dbg->dasm && tilem_disasm_get_label(dbg->dasm, s, a))
162 return TRUE;
163
164 return FALSE;
165 }
166
167 /* Parse user input from hex entry */
hex_entry_parse_value(struct hex_entry * he,TilemDebugger * dbg,int type,dword * value)168 static gboolean hex_entry_parse_value(struct hex_entry *he, TilemDebugger *dbg,
169 int type, dword *value)
170 {
171 const TilemCalc *calc = dbg->emu->calc;
172 dword page;
173 const char *s;
174
175 g_return_val_if_fail(calc != NULL, 0);
176
177 s = gtk_entry_get_text(GTK_ENTRY(he->addr_entry));
178 if (!parse_num(dbg, s, value))
179 return FALSE;
180
181 if (type != TILEM_DB_BREAK_PHYSICAL)
182 return TRUE;
183
184 s = gtk_entry_get_text(GTK_ENTRY(he->page_entry));
185 if (!parse_num(dbg, s, &page))
186 return FALSE;
187
188 *value &= 0x3fff;
189
190 if (page >= calc->hw.rampagemask) {
191 *value += ((page - calc->hw.rampagemask) << 14);
192 *value %= calc->hw.ramsize;
193 *value += calc->hw.romsize;
194 }
195 else {
196 *value += (page << 14);
197 *value %= calc->hw.romsize;
198 }
199
200 return TRUE;
201 }
202
203 /* Parse input fields and check if they make sense */
parse_input(struct breakpoint_dlg * bpdlg,TilemDebugBreakpoint * bp)204 static gboolean parse_input(struct breakpoint_dlg *bpdlg,
205 TilemDebugBreakpoint *bp)
206 {
207 int i;
208 dword addr0, addr1;
209
210 bp->mask = bp->start = bp->end = 0xffffffff;
211 bp->type = get_bp_type(bpdlg);
212 bp->mode = 0;
213
214 for (i = 0; i < 3; i++)
215 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bpdlg->access_cb[i])))
216 bp->mode += (1 << i);
217
218 bp->mode &= type_info[bp->type].access_mask;
219 if (bp->type == TILEM_DB_BREAK_OPCODE)
220 bp->mode = TILEM_DB_BREAK_EXEC;
221 else if (bp->mode == 0)
222 return FALSE;
223
224 if (!hex_entry_parse_value(&bpdlg->start, bpdlg->dbg,
225 bp->type, &addr0))
226 return FALSE;
227
228 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bpdlg->range_rb))) {
229 if (!hex_entry_parse_value(&bpdlg->end, bpdlg->dbg,
230 bp->type, &addr1))
231 return FALSE;
232 }
233 else {
234 addr1 = addr0;
235 }
236
237 if (bp->type == TILEM_DB_BREAK_LOGICAL)
238 bp->mask = 0xffff;
239 else if (bp->type == TILEM_DB_BREAK_PORT)
240 bp->mask = 0xff;
241 else
242 bp->mask = 0xffffffff;
243
244 bp->start = addr0 & bp->mask;
245 bp->end = addr1 & bp->mask;
246 if (bp->end < bp->start)
247 return FALSE;
248
249 return TRUE;
250 }
251
252 /* Check if input fields are valid, and enable/disable OK response as
253 appropriate */
validate(struct breakpoint_dlg * bpdlg)254 static void validate(struct breakpoint_dlg *bpdlg)
255 {
256 TilemDebugBreakpoint tmpbp;
257
258 if (parse_input(bpdlg, &tmpbp))
259 gtk_dialog_set_response_sensitive(GTK_DIALOG(bpdlg->dlg),
260 GTK_RESPONSE_OK, TRUE);
261 else
262 gtk_dialog_set_response_sensitive(GTK_DIALOG(bpdlg->dlg),
263 GTK_RESPONSE_OK, FALSE);
264 }
265
266 /* Enable/disable check buttons for access mode */
set_access_mask(struct breakpoint_dlg * bpdlg,guint mask)267 static void set_access_mask(struct breakpoint_dlg *bpdlg, guint mask)
268 {
269 int i;
270
271 if (mask)
272 gtk_widget_show(bpdlg->access_label);
273 else
274 gtk_widget_hide(bpdlg->access_label);
275
276 for (i = 0; i < 3; i++) {
277 if (mask & (1 << i))
278 gtk_widget_show(bpdlg->access_cb[i]);
279 else
280 gtk_widget_hide(bpdlg->access_cb[i]);
281 }
282 }
283
284 /* Combo box changed */
addr_type_changed(G_GNUC_UNUSED GtkComboBox * combo,gpointer data)285 static void addr_type_changed(G_GNUC_UNUSED GtkComboBox *combo, gpointer data)
286 {
287 struct breakpoint_dlg *bpdlg = data;
288 int type = get_bp_type(bpdlg);
289 gboolean range = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bpdlg->range_rb));
290 char *s;
291
292 s = g_strdup_printf("<b>%s</b>", type_info[type].value_label);
293 gtk_label_set_markup(GTK_LABEL(bpdlg->address_label), s);
294 g_free(s);
295
296 set_access_mask(bpdlg, type_info[type].access_mask);
297
298 if (type_info[type].use_pages) {
299 gtk_widget_show(bpdlg->start.page_label);
300 gtk_widget_show(bpdlg->start.page_entry);
301 }
302 else {
303 gtk_widget_hide(bpdlg->start.page_label);
304 gtk_widget_hide(bpdlg->start.page_entry);
305 }
306
307 if (range) {
308 gtk_label_set_text_with_mnemonic(GTK_LABEL(bpdlg->start.addr_label),
309 "_Start:");
310 gtk_widget_show(bpdlg->end.addr_label);
311 gtk_widget_show(bpdlg->end.addr_entry);
312 }
313 else {
314 gtk_label_set_text_with_mnemonic(GTK_LABEL(bpdlg->start.addr_label),
315 "_Value:");
316 gtk_widget_hide(bpdlg->end.addr_label);
317 gtk_widget_hide(bpdlg->end.addr_entry);
318 }
319
320 if (type_info[type].use_pages && range) {
321 gtk_widget_show(bpdlg->end.page_label);
322 gtk_widget_show(bpdlg->end.page_entry);
323 }
324 else {
325 gtk_widget_hide(bpdlg->end.page_label);
326 gtk_widget_hide(bpdlg->end.page_entry);
327 }
328
329 validate(bpdlg);
330 }
331
332 /* Access mode changed */
access_changed(G_GNUC_UNUSED GtkToggleButton * tb,gpointer data)333 static void access_changed(G_GNUC_UNUSED GtkToggleButton *tb, gpointer data)
334 {
335 struct breakpoint_dlg *bpdlg = data;
336 validate(bpdlg);
337 }
338
339 /* Single/range mode changed */
range_mode_changed(G_GNUC_UNUSED GtkToggleButton * tb,gpointer data)340 static void range_mode_changed(G_GNUC_UNUSED GtkToggleButton *tb, gpointer data)
341 {
342 struct breakpoint_dlg *bpdlg = data;
343 addr_type_changed(NULL, bpdlg);
344 }
345
346 /* Text of entry changed */
entry_edited(G_GNUC_UNUSED GtkEntry * entry,gpointer data)347 static void entry_edited(G_GNUC_UNUSED GtkEntry *entry,
348 gpointer data)
349 {
350 struct breakpoint_dlg *bpdlg = data;
351 validate(bpdlg);
352 }
353
354 /* Key presssed in entry */
entry_key_event(G_GNUC_UNUSED GtkWidget * entry,GdkEventKey * ev,gpointer data)355 static gboolean entry_key_event(G_GNUC_UNUSED GtkWidget *entry,
356 GdkEventKey *ev, gpointer data)
357 {
358 struct breakpoint_dlg *bpdlg = data;
359 TilemDebugBreakpoint tmpbp;
360
361 if (ev->state & GDK_MODIFIER_MASK)
362 return FALSE;
363
364 if (ev->keyval != GDK_Return
365 && ev->keyval != GDK_KP_Enter
366 && ev->keyval != GDK_ISO_Enter)
367 return FALSE;
368
369 if (parse_input(bpdlg, &tmpbp))
370 gtk_dialog_response(GTK_DIALOG(bpdlg->dlg), GTK_RESPONSE_OK);
371 else
372 gtk_widget_child_focus(bpdlg->box, GTK_DIR_TAB_FORWARD);
373
374 return TRUE;
375 }
376
init_hex_entry(struct breakpoint_dlg * bpdlg,struct hex_entry * he,const char * label,GtkTable * tbl,int ypos)377 static void init_hex_entry(struct breakpoint_dlg *bpdlg,
378 struct hex_entry *he, const char *label,
379 GtkTable *tbl, int ypos)
380 {
381 GtkWidget *align, *lbl;
382
383 he->addr_label = lbl = gtk_label_new_with_mnemonic(label);
384 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
385 align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
386 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 6);
387 gtk_container_add(GTK_CONTAINER(align), lbl);
388 gtk_table_attach(tbl, align, 0, 1, ypos, ypos + 1,
389 GTK_FILL, GTK_FILL, 0, 0);
390
391 he->addr_entry = gtk_entry_new();
392 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), he->addr_entry);
393 gtk_table_attach(tbl, he->addr_entry, 1, 2, ypos, ypos + 1,
394 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
395
396 g_signal_connect(he->addr_entry, "changed",
397 G_CALLBACK(entry_edited), bpdlg);
398 g_signal_connect(he->addr_entry, "key-press-event",
399 G_CALLBACK(entry_key_event), bpdlg);
400
401 he->page_label = lbl = gtk_label_new("Page:");
402 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
403 gtk_table_attach(tbl, lbl, 2, 3, ypos, ypos + 1,
404 GTK_FILL, GTK_FILL, 6, 0);
405
406 he->page_entry = gtk_entry_new();
407 gtk_entry_set_width_chars(GTK_ENTRY(he->page_entry), 5);
408 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), he->page_entry);
409 gtk_table_attach(tbl, he->page_entry, 3, 4, ypos, ypos + 1,
410 GTK_FILL, GTK_FILL, 0, 0);
411
412 g_signal_connect(he->page_entry, "changed",
413 G_CALLBACK(entry_edited), bpdlg);
414 g_signal_connect(he->page_entry, "key-press-event",
415 G_CALLBACK(entry_key_event), bpdlg);
416 }
417
edit_breakpoint(TilemDebugger * dbg,GtkWindow * parent_window,const char * title,TilemDebugBreakpoint * bp,gboolean edit_existing)418 static gboolean edit_breakpoint(TilemDebugger *dbg,
419 GtkWindow *parent_window,
420 const char *title,
421 TilemDebugBreakpoint *bp,
422 gboolean edit_existing)
423 {
424 GtkWidget *dlg, *vbox, *frame, *tbl, *hbox, *lbl, *combo, *cb, *rb;
425 struct breakpoint_dlg bpdlg;
426 gsize i;
427
428 g_return_val_if_fail(bp != NULL, FALSE);
429 g_return_val_if_fail(dbg != NULL, FALSE);
430 g_return_val_if_fail(dbg->emu != NULL, FALSE);
431 g_return_val_if_fail(dbg->emu->calc != NULL, FALSE);
432
433 bpdlg.dbg = dbg;
434
435 dlg = gtk_dialog_new_with_buttons(title, parent_window,
436 GTK_DIALOG_MODAL,
437 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
438 GTK_STOCK_OK, GTK_RESPONSE_OK,
439 NULL);
440 gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg),
441 GTK_RESPONSE_OK,
442 GTK_RESPONSE_CANCEL,
443 -1);
444 gtk_dialog_set_default_response(GTK_DIALOG(dlg),
445 GTK_RESPONSE_OK);
446
447 bpdlg.dlg = dlg;
448
449 bpdlg.box = gtk_vbox_new(FALSE, 6);
450 gtk_container_set_border_width(GTK_CONTAINER(bpdlg.box), 6);
451
452 tbl = gtk_table_new(2, 2, FALSE);
453 gtk_table_set_row_spacings(GTK_TABLE(tbl), 6);
454 gtk_table_set_col_spacings(GTK_TABLE(tbl), 6);
455
456 /* Breakpoint type */
457
458 lbl = gtk_label_new_with_mnemonic("Breakpoint _type:");
459 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
460 gtk_table_attach(GTK_TABLE(tbl), lbl, 0, 1, 0, 1,
461 GTK_FILL, GTK_FILL, 0, 0);
462
463 combo = gtk_combo_box_new_text();
464 for (i = 0; i < G_N_ELEMENTS(type_info); i++)
465 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), type_info[i].desc);
466
467 bpdlg.type_combo = combo;
468 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), combo);
469 gtk_table_attach(GTK_TABLE(tbl), combo, 1, 2, 0, 1,
470 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
471
472 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), bp->type);
473
474 /* Access mode */
475
476 bpdlg.access_label = lbl = gtk_label_new("Break when:");
477 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
478 gtk_table_attach(GTK_TABLE(tbl), lbl, 0, 1, 1, 2,
479 GTK_FILL, GTK_FILL, 0, 0);
480
481 hbox = gtk_hbox_new(FALSE, 6);
482
483 cb = gtk_check_button_new_with_mnemonic("_Reading");
484 gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0);
485 bpdlg.access_cb[2] = cb;
486
487 cb = gtk_check_button_new_with_mnemonic("_Writing");
488 gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0);
489 bpdlg.access_cb[1] = cb;
490
491 cb = gtk_check_button_new_with_mnemonic("E_xecuting");
492 gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0);
493 bpdlg.access_cb[0] = cb;
494
495 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bpdlg.access_cb[0]),
496 bp->mode & 1);
497 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bpdlg.access_cb[1]),
498 bp->mode & 2);
499 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bpdlg.access_cb[2]),
500 bp->mode & 4);
501
502 gtk_table_attach(GTK_TABLE(tbl), hbox, 1, 2, 1, 2,
503 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
504
505 frame = new_frame("Breakpoint Condition", tbl);
506 gtk_box_pack_start(GTK_BOX(bpdlg.box), frame, FALSE, FALSE, 0);
507
508 /* Addresses */
509
510 tbl = gtk_table_new(3, 4, FALSE);
511 gtk_table_set_row_spacings(GTK_TABLE(tbl), 6);
512
513 hbox = gtk_hbox_new(FALSE, 6);
514
515 rb = gtk_radio_button_new_with_mnemonic(NULL, "Si_ngle");
516 gtk_box_pack_start(GTK_BOX(hbox), rb, FALSE, FALSE, 0);
517 bpdlg.single_rb = rb;
518
519 rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(rb), "R_ange");
520 gtk_box_pack_start(GTK_BOX(hbox), rb, FALSE, FALSE, 0);
521 bpdlg.range_rb = rb;
522
523 if (edit_existing && bp->end != bp->start)
524 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb), TRUE);
525
526 gtk_table_attach(GTK_TABLE(tbl), hbox, 0, 2, 0, 1,
527 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
528
529 init_hex_entry(&bpdlg, &bpdlg.start, "S_tart:", GTK_TABLE(tbl), 1);
530 init_hex_entry(&bpdlg, &bpdlg.end, "_End:", GTK_TABLE(tbl), 2);
531
532 frame = new_frame("Address", tbl);
533 bpdlg.address_label = gtk_frame_get_label_widget(GTK_FRAME(frame));
534 gtk_box_pack_start(GTK_BOX(bpdlg.box), frame, FALSE, FALSE, 0);
535 gtk_widget_show_all(bpdlg.box);
536
537 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
538 gtk_box_pack_start(GTK_BOX(vbox), bpdlg.box, FALSE, FALSE, 0);
539
540 if (edit_existing) {
541 hex_entry_set_value(&bpdlg.start, dbg, bp->type, bp->start);
542 hex_entry_set_value(&bpdlg.end, dbg, bp->type, bp->end);
543 }
544
545 g_signal_connect(combo, "changed",
546 G_CALLBACK(addr_type_changed), &bpdlg);
547 g_signal_connect(bpdlg.access_cb[0], "toggled",
548 G_CALLBACK(access_changed), &bpdlg);
549 g_signal_connect(bpdlg.access_cb[1], "toggled",
550 G_CALLBACK(access_changed), &bpdlg);
551 g_signal_connect(bpdlg.access_cb[2], "toggled",
552 G_CALLBACK(access_changed), &bpdlg);
553 g_signal_connect(bpdlg.single_rb, "toggled",
554 G_CALLBACK(range_mode_changed), &bpdlg);
555
556 addr_type_changed(NULL, &bpdlg);
557
558 gtk_widget_grab_focus(bpdlg.start.addr_entry);
559
560 do {
561 if (gtk_dialog_run(GTK_DIALOG(dlg)) != GTK_RESPONSE_OK) {
562 gtk_widget_destroy(dlg);
563 return FALSE;
564 }
565 } while (!parse_input(&bpdlg, bp));
566
567 gtk_widget_destroy(dlg);
568 return TRUE;
569 }
570
571 /**************** Breakpoint list dialog ****************/
572
573 enum {
574 COL_BP,
575 COL_START,
576 COL_END,
577 COL_TYPE_STR,
578 COL_START_STR,
579 COL_END_STR,
580 COL_ENABLED,
581 N_COLUMNS
582 };
583
584 struct bplist_dlg {
585 TilemDebugger *dbg;
586 GtkWidget *dlg;
587 GtkListStore *store;
588 GtkWidget *treeview;
589 GtkWidget *remove_btn;
590 GtkWidget *edit_btn;
591 GtkWidget *clear_btn;
592 };
593
594 /* Convert address into a displayable string */
format_address(TilemDebugger * dbg,char * buf,int bufsize,int type,dword value)595 static void format_address(TilemDebugger *dbg,
596 char *buf, int bufsize,
597 int type, dword value)
598 {
599 const TilemCalc *calc;
600 unsigned int page;
601
602 g_return_if_fail(dbg->emu != NULL);
603 g_return_if_fail(dbg->emu->calc != NULL);
604
605 calc = dbg->emu->calc;
606
607 switch (type) {
608 case TILEM_DB_BREAK_LOGICAL:
609 g_snprintf(buf, bufsize, "%04X", value);
610 break;
611
612 case TILEM_DB_BREAK_PHYSICAL:
613 if (value >= calc->hw.romsize) {
614 value -= calc->hw.romsize;
615 page = (value >> 14) + calc->hw.rampagemask;
616 }
617 else {
618 page = (value >> 14);
619 }
620
621 g_snprintf(buf, bufsize, "%02X:%04X", page, value & 0x3fff);
622 break;
623
624 case TILEM_DB_BREAK_PORT:
625 g_snprintf(buf, bufsize, "%02X", value);
626 break;
627
628 case TILEM_DB_BREAK_OPCODE:
629 if (value < 0x100)
630 g_snprintf(buf, bufsize, "%02X", value);
631 else if (value < 0x10000)
632 g_snprintf(buf, bufsize, "%04X", value);
633 else if (value < 0x1000000)
634 g_snprintf(buf, bufsize, "%06X", value);
635 else
636 g_snprintf(buf, bufsize, "%08X", value);
637 break;
638
639 default:
640 g_return_if_reached();
641 }
642 }
643
644 /* Store breakpoint properties in tree model */
set_iter_from_bp(struct bplist_dlg * bpldlg,GtkTreeIter * iter,const TilemDebugBreakpoint * bp)645 static void set_iter_from_bp(struct bplist_dlg *bpldlg, GtkTreeIter *iter,
646 const TilemDebugBreakpoint *bp)
647 {
648 char tbuf[5], sbuf[10], ebuf[10];
649 int i, j;
650
651 g_return_if_fail(bp != NULL);
652
653 tbuf[0] = type_info[bp->type].abbrev;
654 j = 1;
655 for (i = 0; i < 3; i++)
656 if (bp->mode & (4 >> i))
657 tbuf[j++] = "RWX"[i];
658 tbuf[j] = 0;
659
660 format_address(bpldlg->dbg, sbuf, sizeof(sbuf), bp->type, bp->start);
661 format_address(bpldlg->dbg, ebuf, sizeof(ebuf), bp->type, bp->end);
662
663 gtk_list_store_set(bpldlg->store, iter,
664 COL_BP, bp,
665 COL_START, bp->start,
666 COL_END, bp->end,
667 COL_TYPE_STR, tbuf,
668 COL_START_STR, sbuf,
669 COL_END_STR, ebuf,
670 COL_ENABLED, !bp->disabled,
671 -1);
672 }
673
674 /* Get breakpoint pointer for the given tree model row */
get_iter_bp(struct bplist_dlg * bpldlg,GtkTreeIter * iter)675 static TilemDebugBreakpoint *get_iter_bp(struct bplist_dlg *bpldlg,
676 GtkTreeIter *iter)
677 {
678 gpointer ptr;
679 gtk_tree_model_get(GTK_TREE_MODEL(bpldlg->store), iter,
680 COL_BP, &ptr, -1);
681 return (TilemDebugBreakpoint *) ptr;
682 }
683
684 /* Set buttons sensitive or insensitive depending on whether any BPs
685 are selected */
update_buttons(struct bplist_dlg * bpldlg)686 static void update_buttons(struct bplist_dlg *bpldlg)
687 {
688 GtkTreeSelection *sel;
689 gboolean any_sel;
690
691 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bpldlg->treeview));
692
693 any_sel = gtk_tree_selection_get_selected(sel, NULL, NULL);
694 gtk_widget_set_sensitive(bpldlg->remove_btn, any_sel);
695 gtk_widget_set_sensitive(bpldlg->edit_btn, any_sel);
696
697 gtk_widget_set_sensitive(bpldlg->clear_btn,
698 bpldlg->dbg->breakpoints != NULL);
699 }
700
701 /* "Add breakpoint" button clicked */
add_clicked(G_GNUC_UNUSED GtkButton * btn,gpointer data)702 static void add_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data)
703 {
704 struct bplist_dlg *bpldlg = data;
705 TilemDebugBreakpoint tmpbp, *newbp;
706 TilemDebugger *dbg = bpldlg->dbg;
707 GtkTreeIter iter;
708
709 memset(&tmpbp, 0, sizeof(tmpbp));
710 tmpbp.type = dbg->last_bp_type;
711 tmpbp.mode = dbg->last_bp_mode;
712
713 if (!edit_breakpoint(dbg, GTK_WINDOW(bpldlg->dlg),
714 "Add Breakpoint", &tmpbp, FALSE))
715 return;
716
717 dbg->last_bp_type = tmpbp.type;
718 dbg->last_bp_mode = tmpbp.mode;
719
720 newbp = tilem_debugger_add_breakpoint(dbg, &tmpbp);
721 gtk_list_store_append(bpldlg->store, &iter);
722 set_iter_from_bp(bpldlg, &iter, newbp);
723
724 update_buttons(bpldlg);
725 }
726
727 /* "Remove breakpoint" button clicked */
remove_clicked(G_GNUC_UNUSED GtkButton * btn,gpointer data)728 static void remove_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data)
729 {
730 struct bplist_dlg *bpldlg = data;
731 GtkTreeSelection *sel;
732 GtkTreeIter iter;
733 TilemDebugBreakpoint *bp;
734
735 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bpldlg->treeview));
736
737 if (!gtk_tree_selection_get_selected(sel, NULL, &iter))
738 return;
739
740 bp = get_iter_bp(bpldlg, &iter);
741 g_return_if_fail(bp != NULL);
742 gtk_list_store_remove(bpldlg->store, &iter);
743 tilem_debugger_remove_breakpoint(bpldlg->dbg, bp);
744
745 update_buttons(bpldlg);
746 }
747
748 /* Edit an existing breakpoint */
edit_row(struct bplist_dlg * bpldlg,GtkTreeIter * iter)749 static void edit_row(struct bplist_dlg *bpldlg, GtkTreeIter *iter)
750 {
751 TilemDebugBreakpoint *bp, tmpbp;
752
753 bp = get_iter_bp(bpldlg, iter);
754 g_return_if_fail(bp != NULL);
755 tmpbp = *bp;
756
757 if (!edit_breakpoint(bpldlg->dbg, GTK_WINDOW(bpldlg->dlg),
758 "Edit Breakpoint", &tmpbp, TRUE))
759 return;
760
761 tmpbp.disabled = 0;
762 tilem_debugger_change_breakpoint(bpldlg->dbg, bp, &tmpbp);
763 set_iter_from_bp(bpldlg, iter, bp);
764
765 update_buttons(bpldlg);
766 }
767
768 /* "Edit breakpoint" button clicked */
edit_clicked(G_GNUC_UNUSED GtkButton * btn,gpointer data)769 static void edit_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data)
770 {
771 struct bplist_dlg *bpldlg = data;
772 GtkTreeSelection *sel;
773 GtkTreeIter iter;
774
775 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(bpldlg->treeview));
776
777 if (!gtk_tree_selection_get_selected(sel, NULL, &iter))
778 return;
779
780 edit_row(bpldlg, &iter);
781 }
782
783 /* "Clear breakpoints" button clicked */
clear_clicked(G_GNUC_UNUSED GtkButton * btn,gpointer data)784 static void clear_clicked(G_GNUC_UNUSED GtkButton *btn, gpointer data)
785 {
786 struct bplist_dlg *bpldlg = data;
787 GtkWidget *dlg;
788 TilemDebugBreakpoint *bp;
789
790 dlg = gtk_message_dialog_new(GTK_WINDOW(bpldlg->dlg),
791 GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
792 GTK_BUTTONS_NONE,
793 "Clear all breakpoints?");
794 gtk_message_dialog_format_secondary_markup
795 (GTK_MESSAGE_DIALOG(dlg),
796 "All existing breakpoints will be deleted and"
797 " cannot be restored.");
798 gtk_dialog_add_buttons(GTK_DIALOG(dlg),
799 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
800 GTK_STOCK_CLEAR, GTK_RESPONSE_ACCEPT,
801 NULL);
802 gtk_dialog_set_alternative_button_order(GTK_DIALOG(dlg),
803 GTK_RESPONSE_ACCEPT,
804 GTK_RESPONSE_CANCEL,
805 -1);
806
807 if (gtk_dialog_run(GTK_DIALOG(dlg)) == GTK_RESPONSE_ACCEPT) {
808 while (bpldlg->dbg->breakpoints) {
809 bp = bpldlg->dbg->breakpoints->data;
810 tilem_debugger_remove_breakpoint(bpldlg->dbg, bp);
811 }
812 gtk_list_store_clear(bpldlg->store);
813 update_buttons(bpldlg);
814 }
815
816 gtk_widget_destroy(dlg);
817 }
818
819 /* Row activated (double-clicked, usually) */
row_activated(G_GNUC_UNUSED GtkTreeView * treeview,GtkTreePath * path,G_GNUC_UNUSED GtkTreeViewColumn * col,gpointer data)820 static void row_activated(G_GNUC_UNUSED GtkTreeView *treeview,
821 GtkTreePath *path,
822 G_GNUC_UNUSED GtkTreeViewColumn *col,
823 gpointer data)
824 {
825 struct bplist_dlg *bpldlg = data;
826 GtkTreeIter iter;
827
828 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(bpldlg->store),
829 &iter, path))
830 edit_row(bpldlg, &iter);
831 }
832
833 /* Toggle button clicked for a breakpoint */
enabled_toggled(G_GNUC_UNUSED GtkCellRendererToggle * cell,gchar * pathstr,gpointer data)834 static void enabled_toggled(G_GNUC_UNUSED GtkCellRendererToggle *cell,
835 gchar *pathstr, gpointer data)
836 {
837 struct bplist_dlg *bpldlg = data;
838 GtkTreePath *path;
839 GtkTreeIter iter;
840 TilemDebugBreakpoint *bp, tmpbp;
841
842 path = gtk_tree_path_new_from_string(pathstr);
843 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(bpldlg->store),
844 &iter, path)) {
845 gtk_tree_path_free(path);
846 return;
847 }
848 gtk_tree_path_free(path);
849
850 bp = get_iter_bp(bpldlg, &iter);
851 g_return_if_fail(bp != NULL);
852 tmpbp = *bp;
853 tmpbp.disabled = !tmpbp.disabled;
854 tilem_debugger_change_breakpoint(bpldlg->dbg, bp, &tmpbp);
855 set_iter_from_bp(bpldlg, &iter, bp);
856 }
857
858 /* Selection changed */
selection_changed(G_GNUC_UNUSED GtkTreeSelection * sel,gpointer data)859 static void selection_changed(G_GNUC_UNUSED GtkTreeSelection *sel,
860 gpointer data)
861 {
862 struct bplist_dlg *bpldlg = data;
863 update_buttons(bpldlg);
864 }
865
866 /* Show a dialog letting the user add, remove, and edit breakpoints */
tilem_debugger_edit_breakpoints(TilemDebugger * dbg)867 void tilem_debugger_edit_breakpoints(TilemDebugger *dbg)
868 {
869 struct bplist_dlg bpldlg;
870 GtkWidget *dlg, *hbox, *treeview, *sw, *bbox, *btn, *vbox, *vbox2,
871 *invalid_cb, *undoc_cb;
872 GtkListStore *store;
873 GtkTreeViewColumn *col;
874 GtkCellRenderer *cell;
875 GtkTreeIter iter;
876 GSList *l;
877 GtkTreeSelection *sel;
878 unsigned int flags;
879
880 g_return_if_fail(dbg != NULL);
881 g_return_if_fail(dbg->emu != NULL);
882 g_return_if_fail(dbg->emu->calc != NULL);
883
884 bpldlg.dbg = dbg;
885
886 dlg = gtk_dialog_new_with_buttons("Breakpoints",
887 GTK_WINDOW(dbg->window),
888 GTK_DIALOG_MODAL,
889 GTK_STOCK_CLOSE,
890 GTK_RESPONSE_ACCEPT,
891 NULL);
892
893 gtk_window_set_default_size(GTK_WINDOW(dlg), -1, 300);
894
895 store = gtk_list_store_new(N_COLUMNS,
896 G_TYPE_POINTER,
897 G_TYPE_INT,
898 G_TYPE_INT,
899 G_TYPE_STRING,
900 G_TYPE_STRING,
901 G_TYPE_STRING,
902 G_TYPE_BOOLEAN);
903
904 bpldlg.dlg = dlg;
905 bpldlg.store = store;
906
907 vbox = gtk_vbox_new(FALSE, 6);
908 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
909
910 hbox = gtk_hbox_new(FALSE, 6);
911
912 for (l = dbg->breakpoints; l; l = l->next) {
913 gtk_list_store_append(store, &iter);
914 set_iter_from_bp(&bpldlg, &iter, l->data);
915 }
916
917 treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
918 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
919 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE);
920 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE);
921 bpldlg.treeview = treeview;
922
923 fixed_tree_view_init(treeview, 0,
924 COL_TYPE_STR, "MRWX ",
925 COL_START_STR, "DD:DDDD ",
926 COL_END_STR, "DD:DDDD ",
927 COL_ENABLED, TRUE,
928 -1);
929
930 g_signal_connect(treeview, "row-activated",
931 G_CALLBACK(row_activated), &bpldlg);
932
933 /* Enabled/type column */
934
935 col = gtk_tree_view_column_new();
936 gtk_tree_view_column_set_title(col, "Type");
937 gtk_tree_view_column_set_sort_column_id(col, COL_TYPE_STR);
938 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
939
940 cell = gtk_cell_renderer_toggle_new();
941 gtk_tree_view_column_pack_start(col, cell, FALSE);
942 gtk_tree_view_column_set_attributes(col, cell,
943 "active", COL_ENABLED,
944 NULL);
945 g_signal_connect(cell, "toggled",
946 G_CALLBACK(enabled_toggled), &bpldlg);
947
948 cell = gtk_cell_renderer_text_new();
949 gtk_tree_view_column_pack_start(col, cell, TRUE);
950 gtk_tree_view_column_set_attributes(col, cell,
951 "text", COL_TYPE_STR,
952 NULL);
953
954 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
955
956 /* Start column */
957
958 cell = gtk_cell_renderer_text_new();
959 col = gtk_tree_view_column_new_with_attributes
960 ("Start", cell, "text", COL_START_STR, NULL);
961 gtk_tree_view_column_set_sort_column_id(col, COL_START);
962 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
963
964 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
965
966 /* End column */
967
968 cell = gtk_cell_renderer_text_new();
969 col = gtk_tree_view_column_new_with_attributes
970 ("End", cell, "text", COL_END_STR, NULL);
971 gtk_tree_view_column_set_sort_column_id(col, COL_END);
972 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
973
974 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
975
976 sw = gtk_scrolled_window_new(NULL, NULL);
977 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
978 GTK_POLICY_NEVER,
979 GTK_POLICY_AUTOMATIC);
980 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
981 GTK_SHADOW_IN);
982 gtk_container_add(GTK_CONTAINER(sw), treeview);
983
984 gtk_box_pack_start(GTK_BOX(hbox), sw, TRUE, TRUE, 0);
985
986 /* Buttons */
987
988 bbox = gtk_vbutton_box_new();
989 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_START);
990 gtk_box_set_spacing(GTK_BOX(bbox), 6);
991
992 btn = gtk_button_new_from_stock(GTK_STOCK_ADD);
993 g_signal_connect(btn, "clicked",
994 G_CALLBACK(add_clicked), &bpldlg);
995 gtk_container_add(GTK_CONTAINER(bbox), btn);
996
997 btn = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
998 g_signal_connect(btn, "clicked",
999 G_CALLBACK(remove_clicked), &bpldlg);
1000 gtk_container_add(GTK_CONTAINER(bbox), btn);
1001 bpldlg.remove_btn = btn;
1002
1003 btn = gtk_button_new_from_stock(GTK_STOCK_EDIT);
1004 g_signal_connect(btn, "clicked",
1005 G_CALLBACK(edit_clicked), &bpldlg);
1006 gtk_container_add(GTK_CONTAINER(bbox), btn);
1007 bpldlg.edit_btn = btn;
1008
1009 btn = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
1010 g_signal_connect(btn, "clicked",
1011 G_CALLBACK(clear_clicked), &bpldlg);
1012 gtk_container_add(GTK_CONTAINER(bbox), btn);
1013 bpldlg.clear_btn = btn;
1014
1015 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
1016 g_signal_connect(sel, "changed",
1017 G_CALLBACK(selection_changed), &bpldlg);
1018
1019 update_buttons(&bpldlg);
1020
1021 gtk_box_pack_start(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
1022 gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
1023
1024 invalid_cb = gtk_check_button_new_with_mnemonic
1025 ("Break on _invalid instructions");
1026 undoc_cb = gtk_check_button_new_with_mnemonic
1027 ("Break on _undocumented instructions");
1028
1029 tilem_calc_emulator_lock(dbg->emu);
1030 flags = dbg->emu->calc->z80.emuflags;
1031 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(invalid_cb),
1032 (flags & TILEM_Z80_BREAK_INVALID));
1033 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(undoc_cb),
1034 (flags & TILEM_Z80_BREAK_UNDOCUMENTED));
1035 tilem_calc_emulator_unlock(dbg->emu);
1036
1037 gtk_box_pack_start(GTK_BOX(vbox), invalid_cb, FALSE, FALSE, 0);
1038 gtk_box_pack_start(GTK_BOX(vbox), undoc_cb, FALSE, FALSE, 0);
1039
1040 gtk_widget_show_all(vbox);
1041
1042 gtk_widget_grab_focus(treeview);
1043
1044 vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
1045 gtk_box_pack_start(GTK_BOX(vbox2), vbox, TRUE, TRUE, 0);
1046
1047 gtk_dialog_run(GTK_DIALOG(dlg));
1048
1049 tilem_calc_emulator_lock(dbg->emu);
1050 flags = dbg->emu->calc->z80.emuflags;
1051 flags &= ~(TILEM_Z80_BREAK_INVALID | TILEM_Z80_BREAK_UNDOCUMENTED);
1052 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(invalid_cb)))
1053 flags |= TILEM_Z80_BREAK_INVALID;
1054 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(undoc_cb)))
1055 flags |= TILEM_Z80_BREAK_UNDOCUMENTED;
1056 dbg->emu->calc->z80.emuflags = flags;
1057 tilem_calc_emulator_unlock(dbg->emu);
1058
1059 gtk_widget_destroy(dlg);
1060 }
1061