1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2010-2011 Thibault Duponchelle
5 * Copyright (c) 2010-2012 Benjamin Moody
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <gtk/gtk.h>
29 #include <glib/gstdio.h>
30 #include <ticalcs.h>
31 #include <tilem.h>
32 #include <tilemdb.h>
33
34 #include "gui.h"
35 #include "disasmview.h"
36 #include "files.h"
37 #include "msgbox.h"
38 #include "fixedtreeview.h"
39 #include "memmodel.h"
40
41 /* Stack list */
42 enum
43 {
44 COL_OFFSET_STK = 0,
45 COL_VALUE_STK,
46 NUM_COLS_STK
47 };
48
49 /* Indices in reg_entries */
50 enum {
51 R_AF, R_BC, R_DE, R_HL, R_IX, R_SP,
52 R_AF2, R_BC2, R_DE2, R_HL2, R_IY, R_PC,
53 R_IM, R_I,
54 NUM_REGS
55 };
56
57 /* Labels for the entries */
58 static const char * const reg_labels[] = {
59 "A_F:", "B_C:", "D_E:", "H_L:", "I_X:", "SP:",
60 "AF':", "BC':", "DE':", "HL':", "I_Y:", "PC:",
61 "IM:", "I:"
62 };
63
64 /* Labels for the flag buttons */
65 static const char flag_labels[][2] = {
66 "C", "N", "P", "X", "H", "Y", "Z", "S"
67 };
68
69 /* Read a word */
read_mem_word(TilemCalc * calc,dword addr)70 static dword read_mem_word(TilemCalc *calc, dword addr)
71 {
72 dword phys, v;
73
74 phys = (*calc->hw.mem_ltop)(calc, addr & 0xffff);
75 v = calc->mem[phys];
76 phys = (*calc->hw.mem_ltop)(calc, (addr + 1) & 0xffff);
77 v += calc->mem[phys] << 8;
78 return v;
79 }
80
81 /* Determine model name for the purpose of looking up default system
82 symbols */
get_sys_name(const TilemCalc * calc)83 static const char *get_sys_name(const TilemCalc *calc)
84 {
85 g_return_val_if_fail(calc != NULL, NULL);
86
87 switch (calc->hw.model_id) {
88 case TILEM_CALC_TI83:
89 case TILEM_CALC_TI76:
90 return "ti83";
91
92 case TILEM_CALC_TI83P:
93 case TILEM_CALC_TI83P_SE:
94 case TILEM_CALC_TI84P:
95 case TILEM_CALC_TI84P_SE:
96 case TILEM_CALC_TI84P_NSPIRE:
97 return "ti83p";
98
99 default:
100 return calc->hw.name;
101 }
102 }
103
104 /* Load default system symbols */
load_default_symbols(TilemDebugger * dbg)105 static void load_default_symbols(TilemDebugger *dbg)
106 {
107 char *base, *path, *dname;
108 const char *errstr;
109 FILE *symfile;
110
111 base = g_strdup_printf("%s.sym", get_sys_name(dbg->emu->calc));
112 path = get_shared_file_path("symbols", base, NULL);
113 g_free(base);
114 if (!path)
115 return;
116
117 symfile = g_fopen(path, "rb");
118 if (!symfile) {
119 errstr = g_strerror(errno);
120 dname = g_filename_display_name(path);
121 messagebox02(NULL, GTK_MESSAGE_ERROR,
122 "Unable to read symbols",
123 "An error occurred while reading %s: %s",
124 dname, errstr);
125 g_free(dname);
126 g_free(path);
127 return;
128 }
129
130 tilem_disasm_read_symbol_file(dbg->dasm, symfile);
131
132 fclose(symfile);
133 g_free(path);
134 }
135
136 /* Cancel temporary breakpoint */
cancel_step_bp(TilemDebugger * dbg)137 static void cancel_step_bp(TilemDebugger *dbg)
138 {
139 if (!dbg->step_bp)
140 return;
141
142 g_return_if_fail(dbg->emu->calc != NULL);
143 tilem_calc_emulator_lock(dbg->emu);
144 tilem_z80_remove_breakpoint(dbg->emu->calc, dbg->step_bp);
145 tilem_calc_emulator_unlock(dbg->emu);
146 dbg->step_bp = 0;
147 }
148
149 /* Actions */
150
151 /* Run (but leave debugger window open) */
action_run(G_GNUC_UNUSED GtkAction * a,gpointer data)152 static void action_run(G_GNUC_UNUSED GtkAction *a, gpointer data)
153 {
154 TilemDebugger *dbg = data;
155 cancel_step_bp(dbg);
156 tilem_calc_emulator_run(dbg->emu);
157 tilem_debugger_refresh(dbg, TRUE);
158 }
159
160 /* Pause */
action_pause(G_GNUC_UNUSED GtkAction * a,gpointer data)161 static void action_pause(G_GNUC_UNUSED GtkAction *a, gpointer data)
162 {
163 TilemDebugger *dbg = data;
164 tilem_debugger_show(dbg);
165 cancel_step_bp(dbg);
166 }
167
168 /* Stepping */
169
bptest_step(TilemCalc * calc,dword op,G_GNUC_UNUSED void * data)170 static int bptest_step(TilemCalc *calc, dword op, G_GNUC_UNUSED void *data)
171 {
172 /* Single step condition: if calculator is halted, wait until
173 an interrupt occurs; otherwise, stop after any
174 instruction. */
175
176 if (op != 0x76 && (op & ~0x2000) != 0xdd76)
177 /* not a HALT instruction */
178 return 1;
179 else if (calc->z80.interrupts != 0 && calc->z80.r.iff1)
180 return 1;
181 else
182 return 0;
183 }
184
bptest_step_over(TilemCalc * calc,dword op,void * data)185 static int bptest_step_over(TilemCalc *calc, dword op, void *data)
186 {
187 TilemDebugger *dbg = data;
188 dword destaddr;
189
190 /* Step-over condition: behavior depends on what instruction
191 is executed.
192
193 For most instructions, stop when we reach the "next line"
194 as determined by disassembly. This means skipping over
195 CALLs, RSTs, HALTs, and macros.
196
197 For jump and return instructions, stop at the current PC,
198 whatever that is.
199
200 In both cases, wait until we actually reach the target PC,
201 rather than halting immediately; the effect of this is that
202 if an interrupt has occurred, we also "step over" the
203 ISR. */
204
205 if ((op & ~0x20ff) == 0xdd00)
206 op &= 0xff;
207
208 if (op == 0xc3 /* JP */
209 || op == 0xc9 /* RET */
210 || op == 0xe9 /* JP HL/IX/IY */
211 || (op & ~0x38) == 0 /* JR, DJNZ, NOP, or EX AF,AF' */
212 || (op & ~0x38) == 0xc2 /* conditional JP */
213 || (op & ~0x38) == 0xc0 /* conditional RET */
214 || (op & ~0x38) == 0xed45) /* RETI/RETN */
215 destaddr = calc->z80.r.pc.d;
216 else
217 destaddr = dbg->step_next_addr;
218
219 destaddr &= 0xffff;
220
221 /* Delete this breakpoint, and replace it with a simple exec
222 breakpoint at the target address. */
223
224 tilem_z80_remove_breakpoint(calc, dbg->step_bp);
225 dbg->step_bp = tilem_z80_add_breakpoint(calc, TILEM_BREAK_MEM_EXEC,
226 destaddr, destaddr, 0xffff,
227 NULL, NULL);
228 return 0;
229 }
230
bptest_finish(TilemCalc * calc,dword op,void * data)231 static int bptest_finish(TilemCalc *calc, dword op, void *data)
232 {
233 dword exitsp = TILEM_PTR_TO_DWORD(data);
234 byte f;
235
236 /* Finish condition: wait until stack pointer is greater than
237 a certain value, and we execute a return instruction. JP
238 HL/IX/IY are also considered return instructions. */
239
240 if (calc->z80.r.sp.w.l <= exitsp)
241 return 0;
242
243 if ((op & ~0x20ff) == 0xdd00)
244 op &= 0xff;
245
246 f = calc->z80.r.af.b.l;
247
248 switch (op) {
249 case 0xc9: /* RET */
250 case 0xe9: /* JP HL/IX/IY */
251 case 0xed45: /* RETN */
252 case 0xed4d: /* RETI */
253 case 0xed55:
254 case 0xed5d:
255 case 0xed65:
256 case 0xed6d:
257 case 0xed75:
258 case 0xed7d:
259 return 1;
260
261 /* conditionals: check if condition was true */
262 case 0xc0: return !(f & 0x40);
263 case 0xc8: return (f & 0x40);
264 case 0xd0: return !(f & 0x01);
265 case 0xd8: return (f & 0x01);
266 case 0xe0: return !(f & 0x04);
267 case 0xe8: return (f & 0x04);
268 case 0xf0: return !(f & 0x80);
269 case 0xf8: return (f & 0x80);
270
271 default:
272 return 0;
273 }
274 }
275
post_resume_refresh(gpointer data)276 static gboolean post_resume_refresh(gpointer data)
277 {
278 TilemDebugger *dbg = data;
279 tilem_debugger_refresh(dbg, FALSE);
280 return FALSE;
281 }
282
run_with_step_condition(TilemDebugger * dbg,TilemZ80BreakpointFunc func,void * data)283 static void run_with_step_condition(TilemDebugger *dbg,
284 TilemZ80BreakpointFunc func,
285 void *data)
286 {
287 tilem_calc_emulator_lock(dbg->emu);
288 dbg->step_bp = tilem_z80_add_breakpoint(dbg->emu->calc,
289 TILEM_BREAK_EXECUTE, 0, 0, 0,
290 func, data);
291 tilem_calc_emulator_unlock(dbg->emu);
292 tilem_calc_emulator_run(dbg->emu);
293 /* Don't refresh right away, to avoid flickering */
294 g_timeout_add(10, &post_resume_refresh, dbg);
295 }
296
297 /* Execute one instruction */
action_step(G_GNUC_UNUSED GtkAction * a,gpointer data)298 static void action_step(G_GNUC_UNUSED GtkAction *a, gpointer data)
299 {
300 TilemDebugger *dbg = data;
301
302 if (!dbg->emu->paused)
303 return;
304
305 g_return_if_fail(dbg->emu->calc != NULL);
306
307 cancel_step_bp(dbg);
308
309 run_with_step_condition(dbg, &bptest_step, NULL);
310 }
311
312 /* Skip over an instruction */
action_step_over(G_GNUC_UNUSED GtkAction * a,gpointer data)313 static void action_step_over(G_GNUC_UNUSED GtkAction *a, gpointer data)
314 {
315 TilemDebugger *dbg = data;
316
317 if (!dbg->emu->paused)
318 return;
319
320 g_return_if_fail(dbg->emu->calc != NULL);
321
322 cancel_step_bp(dbg);
323
324 tilem_calc_emulator_lock(dbg->emu);
325 tilem_disasm_disassemble(dbg->dasm, dbg->emu->calc, 0,
326 dbg->emu->calc->z80.r.pc.w.l,
327 &dbg->step_next_addr,
328 NULL, 0);
329 tilem_calc_emulator_unlock(dbg->emu);
330
331 run_with_step_condition(dbg, &bptest_step_over, dbg);
332 }
333
334 /* Run until current subroutine finishes */
action_finish(G_GNUC_UNUSED GtkAction * a,gpointer data)335 static void action_finish(G_GNUC_UNUSED GtkAction *a, gpointer data)
336 {
337 TilemDebugger *dbg = data;
338 dword sp;
339
340 if (!dbg->emu->paused)
341 return;
342
343 g_return_if_fail(dbg->emu->calc != NULL);
344
345 cancel_step_bp(dbg);
346
347 tilem_calc_emulator_lock(dbg->emu);
348 sp = dbg->emu->calc->z80.r.sp.w.l;
349 tilem_calc_emulator_unlock(dbg->emu);
350
351 run_with_step_condition(dbg, &bptest_finish,
352 TILEM_DWORD_TO_PTR(sp));
353 }
354
355 /* Toggle breakpoint at selected line */
action_toggle_breakpoint(G_GNUC_UNUSED GtkAction * a,gpointer data)356 static void action_toggle_breakpoint(G_GNUC_UNUSED GtkAction *a, gpointer data)
357 {
358 TilemDebugger *dbg = data;
359 GtkWidget *focus;
360
361 focus = gtk_window_get_focus(GTK_WINDOW(dbg->window));
362 if (TILEM_IS_DISASM_VIEW(focus))
363 tilem_disasm_view_toggle_breakpoint(TILEM_DISASM_VIEW(focus));
364 }
365
366 /* Edit breakpoints */
action_edit_breakpoints(G_GNUC_UNUSED GtkAction * a,gpointer data)367 static void action_edit_breakpoints(G_GNUC_UNUSED GtkAction *a, gpointer data)
368 {
369 TilemDebugger *dbg = data;
370 tilem_debugger_edit_breakpoints(dbg);
371 }
372
373 /* Close debugger window */
action_close(G_GNUC_UNUSED GtkAction * a,gpointer data)374 static void action_close(G_GNUC_UNUSED GtkAction *a, gpointer data)
375 {
376 TilemDebugger *dbg = data;
377 tilem_debugger_hide(dbg);
378 }
379
keypad_dlg_response(G_GNUC_UNUSED GtkDialog * dlg,G_GNUC_UNUSED int response,gpointer data)380 static void keypad_dlg_response(G_GNUC_UNUSED GtkDialog *dlg,
381 G_GNUC_UNUSED int response,
382 gpointer data)
383 {
384 gtk_toggle_action_set_active(data, FALSE);
385 }
386
387 /* Show/hide keypad dialog */
action_view_keypad(GtkToggleAction * action,gpointer data)388 static void action_view_keypad(GtkToggleAction *action, gpointer data)
389 {
390 TilemDebugger *dbg = data;
391
392 if (!dbg->keypad_dialog) {
393 dbg->keypad_dialog = tilem_keypad_dialog_new(dbg);
394 g_signal_connect(dbg->keypad_dialog->window, "response",
395 G_CALLBACK(keypad_dlg_response), action);
396 }
397
398 if (gtk_toggle_action_get_active(action))
399 gtk_window_present(GTK_WINDOW(dbg->keypad_dialog->window));
400 else
401 gtk_widget_hide(dbg->keypad_dialog->window);
402 }
403
404 /* Set memory addressing mode */
action_mem_mode(GtkRadioAction * action,G_GNUC_UNUSED GtkRadioAction * current,gpointer data)405 static void action_mem_mode(GtkRadioAction *action,
406 G_GNUC_UNUSED GtkRadioAction *current,
407 gpointer data)
408 {
409 TilemDebugger *dbg = data;
410
411 dbg->mem_logical = gtk_radio_action_get_current_value(action);
412
413 tilem_disasm_view_set_logical(TILEM_DISASM_VIEW(dbg->disasm_view),
414 dbg->mem_logical);
415
416 tilem_debugger_mem_view_configure(dbg->mem_view,
417 dbg->emu, dbg->mem_rowsize,
418 dbg->mem_start, dbg->mem_logical);
419
420 tilem_config_set("debugger",
421 "mem_logical/b", dbg->mem_logical,
422 NULL);
423 }
424
425 /* Prompt for an address to view */
action_go_to_address(G_GNUC_UNUSED GtkAction * action,gpointer data)426 static void action_go_to_address(G_GNUC_UNUSED GtkAction *action, gpointer data)
427 {
428 TilemDebugger *dbg = data;
429 TilemDisasmView *dv = TILEM_DISASM_VIEW(dbg->disasm_view);
430 dword addr;
431 gboolean addr_set, logical;
432
433 addr_set = tilem_disasm_view_get_cursor(dv, &addr, &logical);
434
435 if (!tilem_prompt_address(dbg, GTK_WINDOW(dbg->window),
436 "Go to Address", "Address:",
437 &addr, !logical, addr_set))
438 return;
439
440 tilem_disasm_view_go_to_address(dv, addr, logical);
441 gtk_widget_grab_focus(dbg->disasm_view);
442 }
443
444 /* Jump to address at given stack index */
go_to_stack_pos(TilemDebugger * dbg,int pos)445 static void go_to_stack_pos(TilemDebugger *dbg, int pos)
446 {
447 dword addr;
448 GtkTreePath *path;
449 GtkTreeSelection *sel;
450
451 dbg->stack_index = pos;
452
453 tilem_calc_emulator_lock(dbg->emu);
454 if (pos < 0)
455 addr = dbg->emu->calc->z80.r.pc.d;
456 else
457 addr = read_mem_word(dbg->emu->calc,
458 dbg->emu->calc->z80.r.sp.d + 2 * pos);
459 tilem_calc_emulator_unlock(dbg->emu);
460
461 tilem_disasm_view_go_to_address(TILEM_DISASM_VIEW(dbg->disasm_view),
462 addr, TRUE);
463 gtk_widget_grab_focus(dbg->disasm_view);
464
465 if (pos >= 0) {
466 path = gtk_tree_path_new_from_indices(pos, -1);
467 gtk_tree_view_set_cursor(GTK_TREE_VIEW(dbg->stack_view),
468 path, NULL, FALSE);
469 gtk_tree_path_free(path);
470 }
471 else {
472 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(dbg->stack_view));
473 gtk_tree_selection_unselect_all(sel);
474 }
475
476 gtk_action_set_sensitive(dbg->prev_stack_action, (pos >= 0));
477 }
478
479 /* Jump to current PC */
action_go_to_pc(G_GNUC_UNUSED GtkAction * action,gpointer data)480 static void action_go_to_pc(G_GNUC_UNUSED GtkAction *action, gpointer data)
481 {
482 TilemDebugger *dbg = data;
483 go_to_stack_pos(dbg, -1);
484 }
485
486 /* Jump to previous stack entry */
action_prev_stack_entry(G_GNUC_UNUSED GtkAction * action,gpointer data)487 static void action_prev_stack_entry(G_GNUC_UNUSED GtkAction *action,
488 gpointer data)
489 {
490 TilemDebugger *dbg = data;
491 if (dbg->stack_index >= 0)
492 go_to_stack_pos(dbg, dbg->stack_index - 1);
493 }
494
495 /* Jump to next stack entry */
action_next_stack_entry(G_GNUC_UNUSED GtkAction * action,gpointer data)496 static void action_next_stack_entry(G_GNUC_UNUSED GtkAction *action,
497 gpointer data)
498 {
499 TilemDebugger *dbg = data;
500 go_to_stack_pos(dbg, dbg->stack_index + 1);
501 }
502
503
504 static const GtkActionEntry run_action_ents[] =
505 {{ "pause", GTK_STOCK_MEDIA_PAUSE, "_Pause", "Escape",
506 "Pause emulation", G_CALLBACK(action_pause) }};
507
508 static const GtkActionEntry paused_action_ents[] =
509 {{ "run", GTK_STOCK_MEDIA_PLAY, "_Run", "F5",
510 "Resume emulation", G_CALLBACK(action_run) },
511 { "step", "tilem-db-step", "_Step", "F7",
512 "Execute one instruction", G_CALLBACK(action_step) },
513 { "step-over", "tilem-db-step-over", "Step _Over", "F8",
514 "Run to the next line (skipping over subroutines)",
515 G_CALLBACK(action_step_over) },
516 { "finish", "tilem-db-finish", "_Finish Subroutine", "F9",
517 "Run to end of the current subroutine", G_CALLBACK(action_finish) },
518 { "toggle-breakpoint", NULL, "Toggle Breakpoint", "F2",
519 "Enable or disable breakpoint at the selected address",
520 G_CALLBACK(action_toggle_breakpoint) },
521 { "edit-breakpoints", NULL, "_Breakpoints", "<control>B",
522 "Add, remove, or modify breakpoints",
523 G_CALLBACK(action_edit_breakpoints) },
524 { "go-to-address", GTK_STOCK_JUMP_TO, "_Address...", "<control>L",
525 "Jump to an address",
526 G_CALLBACK(action_go_to_address) },
527 { "go-to-pc", NULL, "Current P_C", "<alt>Home",
528 "Jump to the current program counter",
529 G_CALLBACK(action_go_to_pc) },
530 { "prev-stack-entry", GTK_STOCK_GO_UP, "_Previous Stack Entry", "<alt>Page_Up",
531 "Jump to the previous address in the stack",
532 G_CALLBACK(action_prev_stack_entry) },
533 { "next-stack-entry", GTK_STOCK_GO_DOWN, "_Next Stack Entry", "<alt>Page_Down",
534 "Jump to the next address in the stack",
535 G_CALLBACK(action_next_stack_entry) }};
536
537 static const GtkRadioActionEntry mem_mode_ents[] =
538 {{ "view-logical", 0, "_Logical Addresses", 0,
539 "Show contents of the current Z80 address space", 1 },
540 { "view-absolute", 0, "_Absolute Addresses", 0,
541 "Show all memory contents", 0 }};
542
543 static const GtkActionEntry misc_action_ents[] =
544 {{ "debug-menu", 0, "_Debug", 0, 0, 0 },
545 { "view-menu", 0, "_View", 0, 0, 0 },
546 { "go-menu", 0, "_Go", 0, 0, 0 },
547 { "close", GTK_STOCK_CLOSE, 0, 0,
548 "Close the debugger", G_CALLBACK(action_close) }};
549
550 static const GtkToggleActionEntry misc_toggle_ents[] =
551 {{ "view-keypad", 0, "_Keypad", 0,
552 "Show the calculator keypad state",
553 G_CALLBACK(action_view_keypad), FALSE }};
554
555 /* Callbacks */
556
557 /* Register edited */
reg_edited(GtkEntry * ent,gpointer data)558 static void reg_edited(GtkEntry *ent, gpointer data)
559 {
560 TilemDebugger *dbg = data;
561 TilemCalc *calc;
562 const char *text;
563 char *end;
564 dword value;
565 int i;
566
567 if (dbg->refreshing)
568 return;
569
570 calc = dbg->emu->calc;
571 g_return_if_fail(calc != NULL);
572
573 text = gtk_entry_get_text(ent);
574 value = strtol(text, &end, 16);
575
576 for (i = 0; i < NUM_REGS; i++)
577 if (ent == (GtkEntry*) dbg->reg_entries[i])
578 break;
579
580 tilem_calc_emulator_lock(dbg->emu);
581 switch (i) {
582 case R_AF: calc->z80.r.af.d = value; break;
583 case R_BC: calc->z80.r.bc.d = value; break;
584 case R_DE: calc->z80.r.de.d = value; break;
585 case R_HL: calc->z80.r.hl.d = value; break;
586 case R_AF2: calc->z80.r.af2.d = value; break;
587 case R_BC2: calc->z80.r.bc2.d = value; break;
588 case R_DE2: calc->z80.r.de2.d = value; break;
589 case R_HL2: calc->z80.r.hl2.d = value; break;
590 case R_SP: calc->z80.r.sp.d = value; break;
591 case R_PC: calc->z80.r.pc.d = value; break;
592 case R_IX: calc->z80.r.ix.d = value; break;
593 case R_IY: calc->z80.r.iy.d = value; break;
594 case R_I: calc->z80.r.ir.b.h = value; break;
595 }
596 tilem_calc_emulator_unlock(dbg->emu);
597
598 /* Set the value of the register immediately, but don't
599 refresh the display: refreshing the registers themselves
600 while user is trying to edit them would just be obnoxious,
601 and refreshing stack and disassembly would be at least
602 distracting. Instead, we'll refresh only when focus
603 changes. */
604
605 dbg->delayed_refresh = TRUE;
606 }
607
608 /* Flag button toggled */
flag_edited(GtkToggleButton * btn,gpointer data)609 static void flag_edited(GtkToggleButton *btn, gpointer data)
610 {
611 TilemDebugger *dbg = data;
612 TilemCalc *calc;
613 int i;
614
615 if (dbg->refreshing)
616 return;
617
618 calc = dbg->emu->calc;
619 g_return_if_fail(calc != NULL);
620
621 for (i = 0; i < 8; i++)
622 if (btn == (GtkToggleButton*) dbg->flag_buttons[i])
623 break;
624
625 tilem_calc_emulator_lock(dbg->emu);
626 if (gtk_toggle_button_get_active(btn))
627 calc->z80.r.af.d |= (1 << i);
628 else
629 calc->z80.r.af.d &= ~(1 << i);
630 tilem_calc_emulator_unlock(dbg->emu);
631
632 /* refresh AF */
633 tilem_debugger_refresh(dbg, FALSE);
634 }
635
636 /* IM edited */
im_edited(GtkEntry * ent,gpointer data)637 static void im_edited(GtkEntry *ent, gpointer data)
638 {
639 TilemDebugger *dbg = data;
640 TilemCalc *calc;
641 const char *text;
642 char *end;
643 int value;
644
645 if (dbg->refreshing)
646 return;
647
648 calc = dbg->emu->calc;
649 g_return_if_fail(calc != NULL);
650
651 text = gtk_entry_get_text(ent);
652 value = strtol(text, &end, 0);
653
654 tilem_calc_emulator_lock(dbg->emu);
655 if (value >= 0 && value <= 2)
656 calc->z80.r.im = value;
657 tilem_calc_emulator_unlock(dbg->emu);
658 /* no need to refresh */
659 }
660
661 /* IFF button toggled */
iff_edited(GtkToggleButton * btn,gpointer data)662 static void iff_edited(GtkToggleButton *btn, gpointer data)
663 {
664 TilemDebugger *dbg = data;
665 TilemCalc *calc;
666
667 if (dbg->refreshing)
668 return;
669
670 calc = dbg->emu->calc;
671 g_return_if_fail(calc != NULL);
672
673 tilem_calc_emulator_lock(dbg->emu);
674 if (gtk_toggle_button_get_active(btn))
675 calc->z80.r.iff1 = calc->z80.r.iff2 = 1;
676 else
677 calc->z80.r.iff1 = calc->z80.r.iff2 = 0;
678 tilem_calc_emulator_unlock(dbg->emu);
679 /* no need to refresh */
680 }
681
682 /* Main window's focus widget changed */
focus_changed(G_GNUC_UNUSED GtkWindow * win,G_GNUC_UNUSED GtkWidget * widget,gpointer data)683 static void focus_changed(G_GNUC_UNUSED GtkWindow *win,
684 G_GNUC_UNUSED GtkWidget *widget,
685 gpointer data)
686 {
687 TilemDebugger *dbg = data;
688
689 /* delayed refresh - see reg_edited() above */
690 if (dbg->delayed_refresh)
691 tilem_debugger_refresh(dbg, FALSE);
692 }
693
694 /* Main window received a "delete" message */
delete_win(G_GNUC_UNUSED GtkWidget * w,G_GNUC_UNUSED GdkEvent * ev,gpointer data)695 static gboolean delete_win(G_GNUC_UNUSED GtkWidget *w,
696 G_GNUC_UNUSED GdkEvent *ev,
697 gpointer data)
698 {
699 TilemDebugger *dbg = data;
700 tilem_debugger_hide(dbg);
701 return TRUE;
702 }
703
704 /* Create table of widgets for editing registers */
create_registers(TilemDebugger * dbg)705 static GtkWidget *create_registers(TilemDebugger *dbg)
706 {
707 GtkWidget *vbox, *tbl, *lbl, *hbox, *ent, *btn;
708 int i;
709
710 vbox = gtk_vbox_new(FALSE, 6);
711
712 tbl = gtk_table_new(6, 4, FALSE);
713 gtk_table_set_row_spacings(GTK_TABLE(tbl), 6);
714 gtk_table_set_col_spacings(GTK_TABLE(tbl), 6);
715 gtk_table_set_col_spacing(GTK_TABLE(tbl), 1, 12);
716
717 for (i = 0; i < 12; i++) {
718 lbl = gtk_label_new_with_mnemonic(reg_labels[i]);
719 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
720 gtk_table_attach(GTK_TABLE(tbl), lbl,
721 2 * (i / 6), 2 * (i / 6) + 1,
722 (i % 6), (i % 6) + 1,
723 GTK_FILL, GTK_FILL, 0, 0);
724
725 dbg->reg_entries[i] = ent = gtk_entry_new();
726 gtk_entry_set_width_chars(GTK_ENTRY(ent), 5);
727 g_signal_connect(ent, "changed", G_CALLBACK(reg_edited), dbg);
728 gtk_table_attach(GTK_TABLE(tbl), ent,
729 2 * (i / 6) + 1, 2 * (i / 6) + 2,
730 (i % 6), (i % 6) + 1,
731 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
732
733 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ent);
734 }
735
736 gtk_box_pack_start(GTK_BOX(vbox), tbl, FALSE, FALSE, 0);
737
738 hbox = gtk_hbox_new(TRUE, 0);
739
740 for (i = 7; i >= 0; i--) {
741 btn = gtk_toggle_button_new_with_label(flag_labels[i]);
742 dbg->flag_buttons[i] = btn;
743 g_signal_connect(btn, "toggled", G_CALLBACK(flag_edited), dbg);
744 gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
745 }
746
747 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
748
749 hbox = gtk_hbox_new(FALSE, 6);
750
751 for (i = 12; i < 14; i++) {
752 lbl = gtk_label_new(reg_labels[i]);
753 gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0);
754
755 dbg->reg_entries[i] = ent = gtk_entry_new();
756 gtk_box_pack_start(GTK_BOX(hbox), ent, TRUE, TRUE, 0);
757 }
758
759 g_signal_connect(dbg->reg_entries[R_I], "changed",
760 G_CALLBACK(reg_edited), dbg);
761 g_signal_connect(dbg->reg_entries[R_IM], "changed",
762 G_CALLBACK(im_edited), dbg);
763
764 gtk_entry_set_width_chars(GTK_ENTRY(dbg->reg_entries[R_IM]), 2);
765 gtk_entry_set_width_chars(GTK_ENTRY(dbg->reg_entries[R_I]), 3);
766
767 dbg->iff_checkbox = btn = gtk_check_button_new_with_label("EI");
768 g_signal_connect(btn, "toggled", G_CALLBACK(iff_edited), dbg);
769 gtk_box_pack_start(GTK_BOX(hbox), btn, TRUE, TRUE, 0);
770
771 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
772
773 return vbox;
774 }
775
776 /* Create the GtkTreeView to show the stack */
create_stack_view()777 static GtkWidget *create_stack_view()
778 {
779 GtkCellRenderer *renderer;
780 GtkWidget *treeview;
781 GtkTreeViewColumn *column;
782
783 /* Create the stack list tree view and set title invisible */
784 treeview = gtk_tree_view_new();
785 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
786 gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), TRUE);
787 gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview),
788 COL_VALUE_STK);
789
790 /* Create the columns */
791 renderer = gtk_cell_renderer_text_new ();
792 column = gtk_tree_view_column_new_with_attributes
793 ("ADDR", renderer, "text", COL_OFFSET_STK, NULL);
794
795 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
796 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
797
798 renderer = gtk_cell_renderer_text_new ();
799 column = gtk_tree_view_column_new_with_attributes
800 ("VAL", renderer, "text", COL_VALUE_STK, NULL);
801
802 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
803 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
804
805 return treeview;
806 }
807
808 /* Create a new scrolled window with sensible default settings. */
new_scrolled_window(GtkWidget * contents)809 static GtkWidget *new_scrolled_window(GtkWidget *contents)
810 {
811 GtkWidget *sw;
812 sw = gtk_scrolled_window_new(NULL, NULL);
813 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
814 GTK_POLICY_AUTOMATIC,
815 GTK_POLICY_AUTOMATIC);
816 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
817 GTK_SHADOW_IN);
818 gtk_container_add(GTK_CONTAINER(sw), contents);
819 return sw;
820 }
821
822 static const char uidesc[] =
823 "<menubar name='menu-bar'>"
824 " <menu action='debug-menu'>"
825 " <menuitem action='run'/>"
826 " <menuitem action='pause'/>"
827 " <separator/>"
828 " <menuitem action='step'/>"
829 " <menuitem action='step-over'/>"
830 " <menuitem action='finish'/>"
831 " <separator/>"
832 " <menuitem action='edit-breakpoints'/>"
833 " <separator/>"
834 " <menuitem action='close'/>"
835 " </menu>"
836 " <menu action='view-menu'>"
837 " <menuitem action='view-keypad'/>"
838 " <separator/>"
839 " <menuitem action='view-logical'/>"
840 " <menuitem action='view-absolute'/>"
841 " </menu>"
842 " <menu action='go-menu'>"
843 " <menuitem action='go-to-address'/>"
844 " <menuitem action='go-to-pc'/>"
845 " <separator/>"
846 " <menuitem action='prev-stack-entry'/>"
847 " <menuitem action='next-stack-entry'/>"
848 " </menu>"
849 "</menubar>"
850 "<toolbar name='toolbar'>"
851 " <toolitem action='run'/>"
852 " <toolitem action='pause'/>"
853 " <separator/>"
854 " <toolitem action='step'/>"
855 " <toolitem action='step-over'/>"
856 " <toolitem action='finish'/>"
857 "</toolbar>"
858 "<accelerator action='toggle-breakpoint'/>";
859
860 /* Create a new TilemDebugger. */
tilem_debugger_new(TilemCalcEmulator * emu)861 TilemDebugger *tilem_debugger_new(TilemCalcEmulator *emu)
862 {
863 TilemDebugger *dbg;
864 GtkWidget *hbox, *vbox, *vbox2, *vpaned, *sw, *menubar, *toolbar;
865 GtkUIManager *uimgr;
866 GtkAccelGroup *accelgrp;
867 GError *err = NULL;
868 int defwidth, defheight;
869
870 g_return_val_if_fail(emu != NULL, NULL);
871
872 dbg = g_slice_new0(TilemDebugger);
873 dbg->emu = emu;
874 dbg->dasm = tilem_disasm_new();
875
876 dbg->last_bp_type = TILEM_DB_BREAK_LOGICAL;
877 dbg->last_bp_mode = TILEM_DB_BREAK_EXEC;
878
879 dbg->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
880 gtk_window_set_title(GTK_WINDOW(dbg->window), "TilEm Debugger");
881 gtk_window_set_role(GTK_WINDOW(dbg->window), "Debugger");
882
883 tilem_config_get("debugger",
884 "width/i", &defwidth,
885 "height/i", &defheight,
886 "mem_width/i", &dbg->mem_rowsize,
887 "mem_start/i", &dbg->mem_start,
888 "mem_logical/b=1", &dbg->mem_logical,
889 NULL);
890
891 if (dbg->mem_rowsize <= 0)
892 dbg->mem_rowsize = 8;
893 if (dbg->mem_start < 0 || dbg->mem_start >= dbg->mem_rowsize)
894 dbg->mem_start = 0;
895
896 if (defwidth <= 0 || defheight <= 0) {
897 defwidth = 600;
898 defheight = 400;
899 }
900
901 gtk_window_set_default_size(GTK_WINDOW(dbg->window),
902 defwidth, defheight);
903
904 g_signal_connect(dbg->window, "set-focus",
905 G_CALLBACK(focus_changed), dbg);
906 g_signal_connect(dbg->window, "delete-event",
907 G_CALLBACK(delete_win), dbg);
908
909 vbox2 = gtk_vbox_new(FALSE, 0);
910
911 /* Actions and menu bar */
912
913 uimgr = gtk_ui_manager_new();
914 dbg->run_actions = gtk_action_group_new("Debug");
915 gtk_action_group_add_actions(dbg->run_actions, run_action_ents,
916 G_N_ELEMENTS(run_action_ents), dbg);
917 gtk_ui_manager_insert_action_group(uimgr, dbg->run_actions, 0);
918
919 dbg->paused_actions = gtk_action_group_new("Debug");
920 gtk_action_group_add_actions(dbg->paused_actions, paused_action_ents,
921 G_N_ELEMENTS(paused_action_ents), dbg);
922 gtk_action_group_add_radio_actions(dbg->paused_actions, mem_mode_ents,
923 G_N_ELEMENTS(mem_mode_ents),
924 dbg->mem_logical,
925 G_CALLBACK(action_mem_mode), dbg);
926 gtk_ui_manager_insert_action_group(uimgr, dbg->paused_actions, 0);
927
928 dbg->misc_actions = gtk_action_group_new("Debug");
929 gtk_action_group_add_actions(dbg->misc_actions, misc_action_ents,
930 G_N_ELEMENTS(misc_action_ents), dbg);
931 gtk_action_group_add_toggle_actions(dbg->misc_actions, misc_toggle_ents,
932 G_N_ELEMENTS(misc_toggle_ents), dbg);
933 gtk_ui_manager_insert_action_group(uimgr, dbg->misc_actions, 0);
934
935 dbg->prev_stack_action = gtk_action_group_get_action(dbg->paused_actions,
936 "prev-stack-entry");
937
938 accelgrp = gtk_ui_manager_get_accel_group(uimgr);
939 gtk_window_add_accel_group(GTK_WINDOW(dbg->window), accelgrp);
940
941 if (!gtk_ui_manager_add_ui_from_string(uimgr, uidesc, -1, &err))
942 g_error("Failed to create menus: %s", err->message);
943
944 menubar = gtk_ui_manager_get_widget(uimgr, "/menu-bar");
945 gtk_box_pack_start(GTK_BOX(vbox2), menubar, FALSE, FALSE, 0);
946
947 toolbar = gtk_ui_manager_get_widget(uimgr, "/toolbar");
948 gtk_box_pack_start(GTK_BOX(vbox2), toolbar, FALSE, FALSE, 0);
949
950 g_object_unref(uimgr);
951
952 hbox = gtk_hbox_new(FALSE, 6);
953
954 vpaned = gtk_vpaned_new();
955
956 /* Disassembly view */
957
958 dbg->disasm_view = tilem_disasm_view_new(dbg);
959 tilem_disasm_view_set_logical(TILEM_DISASM_VIEW(dbg->disasm_view),
960 dbg->mem_logical);
961 sw = new_scrolled_window(dbg->disasm_view);
962 gtk_paned_pack1(GTK_PANED(vpaned), sw, TRUE, TRUE);
963
964 /* Memory view */
965
966 dbg->mem_view = tilem_debugger_mem_view_new(dbg);
967 sw = new_scrolled_window(dbg->mem_view);
968 gtk_paned_pack2(GTK_PANED(vpaned), sw, TRUE, TRUE);
969
970 gtk_box_pack_start(GTK_BOX(hbox), vpaned, TRUE, TRUE, 0);
971
972 vbox = gtk_vbox_new(FALSE, 6);
973
974 /* Registers */
975
976 dbg->regbox = create_registers(dbg);
977 gtk_box_pack_start(GTK_BOX(vbox), dbg->regbox, FALSE, FALSE, 0);
978
979 /* Stack view */
980
981 dbg->stack_view = create_stack_view();
982 sw = new_scrolled_window(dbg->stack_view);
983 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
984
985 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
986
987 gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0);
988 gtk_widget_show_all(vbox2);
989
990 gtk_container_add(GTK_CONTAINER(dbg->window), vbox2);
991
992 tilem_debugger_calc_changed(dbg);
993
994 return dbg;
995 }
996
997 /* Save the dimension for the debugger */
save_debugger_dimension(TilemDebugger * dbg)998 static void save_debugger_dimension(TilemDebugger *dbg)
999 {
1000 gint width, height;
1001
1002 if (!dbg->window)
1003 return;
1004
1005 gtk_window_get_size(GTK_WINDOW(dbg->window), &width, &height);
1006
1007 if (width <= 0 || height <= 0)
1008 return;
1009
1010 tilem_config_set("debugger",
1011 "width/i", width,
1012 "height/i", height,
1013 NULL);
1014 }
1015
free_all_breakpoints(TilemDebugger * dbg)1016 static void free_all_breakpoints(TilemDebugger *dbg)
1017 {
1018 GSList *l;
1019 TilemDebugBreakpoint *bp;
1020
1021 for (l = dbg->breakpoints; l; l = l->next) {
1022 bp = l->data;
1023 g_slice_free(TilemDebugBreakpoint, bp);
1024 }
1025
1026 g_slist_free(dbg->breakpoints);
1027 dbg->breakpoints = NULL;
1028 }
1029
1030 /* Free a TilemDebugger. */
tilem_debugger_free(TilemDebugger * dbg)1031 void tilem_debugger_free(TilemDebugger *dbg)
1032 {
1033 g_return_if_fail(dbg != NULL);
1034
1035 save_debugger_dimension(dbg);
1036
1037 if (dbg->emu && dbg->emu->dbg == dbg)
1038 dbg->emu->dbg = NULL;
1039 if (dbg->window) {
1040 gtk_widget_destroy(dbg->window);
1041 }
1042 if (dbg->dasm)
1043 tilem_disasm_free(dbg->dasm);
1044 if (dbg->run_actions)
1045 g_object_unref(dbg->run_actions);
1046 if (dbg->paused_actions)
1047 g_object_unref(dbg->paused_actions);
1048 if (dbg->misc_actions)
1049 g_object_unref(dbg->misc_actions);
1050
1051 free_all_breakpoints(dbg);
1052
1053 g_slice_free(TilemDebugger, dbg);
1054 }
1055
entry_printf(GtkWidget * ent,const char * s,...)1056 static void entry_printf(GtkWidget *ent, const char *s, ...)
1057 {
1058 char buf[20];
1059 va_list ap;
1060 va_start(ap, s);
1061 g_vsnprintf(buf, sizeof(buf), s, ap);
1062 va_end(ap);
1063 gtk_entry_set_text(GTK_ENTRY(ent), buf);
1064 }
1065
refresh_regs(TilemDebugger * dbg)1066 static void refresh_regs(TilemDebugger *dbg)
1067 {
1068 TilemCalc *calc = dbg->emu->calc;
1069 int i;
1070 GtkToggleButton *btn;
1071
1072 entry_printf(dbg->reg_entries[R_AF], "%04X", calc->z80.r.af.w.l);
1073 entry_printf(dbg->reg_entries[R_BC], "%04X", calc->z80.r.bc.w.l);
1074 entry_printf(dbg->reg_entries[R_DE], "%04X", calc->z80.r.de.w.l);
1075 entry_printf(dbg->reg_entries[R_HL], "%04X", calc->z80.r.hl.w.l);
1076 entry_printf(dbg->reg_entries[R_AF2], "%04X", calc->z80.r.af2.w.l);
1077 entry_printf(dbg->reg_entries[R_BC2], "%04X", calc->z80.r.bc2.w.l);
1078 entry_printf(dbg->reg_entries[R_DE2], "%04X", calc->z80.r.de2.w.l);
1079 entry_printf(dbg->reg_entries[R_HL2], "%04X", calc->z80.r.hl2.w.l);
1080 entry_printf(dbg->reg_entries[R_SP], "%04X", calc->z80.r.sp.w.l);
1081 entry_printf(dbg->reg_entries[R_PC], "%04X", calc->z80.r.pc.w.l);
1082 entry_printf(dbg->reg_entries[R_IX], "%04X", calc->z80.r.ix.w.l);
1083 entry_printf(dbg->reg_entries[R_IY], "%04X", calc->z80.r.iy.w.l);
1084 entry_printf(dbg->reg_entries[R_I], "%02X", calc->z80.r.ir.b.h);
1085 entry_printf(dbg->reg_entries[R_IM], "%d", calc->z80.r.im);
1086
1087 for (i = 0; i < 8; i++) {
1088 btn = GTK_TOGGLE_BUTTON(dbg->flag_buttons[i]);
1089 gtk_toggle_button_set_active(btn, calc->z80.r.af.d & (1 << i));
1090 }
1091
1092 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dbg->iff_checkbox),
1093 calc->z80.r.iff1);
1094 }
1095
1096 /* Create GtkListStore and attach it */
fill_stk_list(TilemDebugger * dbg)1097 static GtkTreeModel* fill_stk_list(TilemDebugger *dbg)
1098 {
1099 GtkListStore *store;
1100 GtkTreeIter iter;
1101 char stack_offset[10];
1102 char stack_value[10];
1103 dword i, v;
1104 int n = 0;
1105
1106 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
1107 i = dbg->emu->calc->z80.r.sp.w.l;
1108 while (i < 0x10000 && n < 512) {
1109 g_snprintf(stack_offset, sizeof(stack_offset), "%04X:", i);
1110
1111 v = read_mem_word(dbg->emu->calc, i);
1112 g_snprintf(stack_value, sizeof(stack_value), "%04X", v);
1113
1114 gtk_list_store_append(store, &iter);
1115 gtk_list_store_set(store, &iter,
1116 COL_OFFSET_STK, stack_offset,
1117 COL_VALUE_STK, stack_value, -1);
1118
1119 i += 0x0002;
1120 n++;
1121 }
1122
1123 return GTK_TREE_MODEL (store);
1124 }
1125
refresh_stack(TilemDebugger * dbg)1126 static void refresh_stack(TilemDebugger *dbg)
1127 {
1128 GtkTreeModel *model = fill_stk_list(dbg);
1129 gtk_tree_view_set_model(GTK_TREE_VIEW(dbg->stack_view), model);
1130 g_object_unref(model);
1131
1132 fixed_tree_view_init(dbg->stack_view, 0,
1133 COL_OFFSET_STK, "DDDD: ",
1134 COL_VALUE_STK, "DDDD ",
1135 -1);
1136 }
1137
unselect_all(GtkTreeView * tv)1138 static void unselect_all(GtkTreeView *tv)
1139 {
1140 GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);
1141 gtk_tree_selection_unselect_all(sel);
1142 }
1143
refresh_all(TilemDebugger * dbg,gboolean updatemem)1144 static void refresh_all(TilemDebugger *dbg, gboolean updatemem)
1145 {
1146 TilemCalc *calc;
1147 gboolean paused;
1148 gboolean updatedasm = FALSE;
1149 GtkTreeModel *model;
1150
1151 dbg->refreshing = TRUE;
1152 dbg->delayed_refresh = FALSE;
1153
1154 tilem_calc_emulator_lock(dbg->emu);
1155 calc = dbg->emu->calc;
1156 paused = dbg->emu->paused;
1157
1158 if (calc) {
1159 refresh_regs(dbg);
1160
1161 if (dbg->lastwrite != calc->z80.lastwrite)
1162 updatemem = TRUE;
1163
1164 if (updatemem || dbg->lastsp != calc->z80.r.sp.d)
1165 refresh_stack(dbg);
1166
1167 if (paused && dbg->lastpc != calc->z80.r.pc.d)
1168 updatedasm = TRUE;
1169
1170 dbg->lastwrite = calc->z80.lastwrite;
1171 dbg->lastsp = calc->z80.r.sp.d;
1172 dbg->lastpc = calc->z80.r.pc.d;
1173 }
1174
1175 tilem_calc_emulator_unlock(dbg->emu);
1176
1177 model = gtk_tree_view_get_model(GTK_TREE_VIEW(dbg->mem_view));
1178 tilem_mem_model_clear_cache(TILEM_MEM_MODEL(model));
1179
1180 gtk_widget_queue_draw(dbg->mem_view);
1181
1182 if (paused != dbg->paused) {
1183 dbg->paused = paused;
1184 gtk_widget_set_sensitive(dbg->regbox, paused);
1185 gtk_widget_set_sensitive(dbg->disasm_view, paused);
1186 gtk_widget_set_sensitive(dbg->mem_view, paused);
1187 gtk_widget_set_sensitive(dbg->stack_view, paused);
1188 gtk_action_group_set_sensitive(dbg->run_actions, !paused);
1189 gtk_action_group_set_sensitive(dbg->paused_actions, paused);
1190 updatedasm = TRUE; /* need to redraw icons */
1191 }
1192
1193 if (updatemem || updatedasm)
1194 tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view));
1195
1196 if (!paused) {
1197 unselect_all(GTK_TREE_VIEW(dbg->disasm_view));
1198 unselect_all(GTK_TREE_VIEW(dbg->mem_view));
1199 unselect_all(GTK_TREE_VIEW(dbg->stack_view));
1200 }
1201
1202 if (dbg->keypad_dialog)
1203 tilem_keypad_dialog_refresh(dbg->keypad_dialog);
1204
1205 dbg->refreshing = FALSE;
1206 }
1207
1208 /* Show debugger, and pause emulator if not already paused. */
tilem_debugger_show(TilemDebugger * dbg)1209 void tilem_debugger_show(TilemDebugger *dbg)
1210 {
1211 g_return_if_fail(dbg != NULL);
1212 g_return_if_fail(dbg->emu->calc != NULL);
1213 tilem_calc_emulator_pause(dbg->emu);
1214 cancel_step_bp(dbg);
1215 refresh_all(dbg, TRUE);
1216 go_to_stack_pos(dbg, -1);
1217 gtk_window_present(GTK_WINDOW(dbg->window));
1218 }
1219
1220 /* Hide debugger, and resume emulation if not already running. */
tilem_debugger_hide(TilemDebugger * dbg)1221 void tilem_debugger_hide(TilemDebugger *dbg)
1222 {
1223 g_return_if_fail(dbg != NULL);
1224 gtk_widget_hide(dbg->window);
1225 tilem_calc_emulator_run(dbg->emu);
1226 save_debugger_dimension(dbg);
1227 }
1228
1229 /* New calculator loaded. */
tilem_debugger_calc_changed(TilemDebugger * dbg)1230 void tilem_debugger_calc_changed(TilemDebugger *dbg)
1231 {
1232 TilemCalc *calc;
1233
1234 g_return_if_fail(dbg != NULL);
1235
1236 tilem_disasm_free(dbg->dasm);
1237 dbg->dasm = tilem_disasm_new();
1238
1239 dbg->step_bp = 0;
1240
1241 free_all_breakpoints(dbg);
1242
1243 calc = dbg->emu->calc;
1244 if (!calc)
1245 return;
1246
1247 load_default_symbols(dbg);
1248
1249 tilem_debugger_mem_view_configure(dbg->mem_view,
1250 dbg->emu, dbg->mem_rowsize,
1251 dbg->mem_start, dbg->mem_logical);
1252
1253 tilem_debugger_refresh(dbg, TRUE);
1254
1255 if (dbg->keypad_dialog)
1256 tilem_keypad_dialog_calc_changed(dbg->keypad_dialog);
1257 }
1258
1259 /* Update display. */
tilem_debugger_refresh(TilemDebugger * dbg,gboolean updatemem)1260 void tilem_debugger_refresh(TilemDebugger *dbg, gboolean updatemem)
1261 {
1262 g_return_if_fail(dbg != NULL);
1263
1264 if (!gtk_widget_get_visible(dbg->window))
1265 return;
1266
1267 refresh_all(dbg, updatemem);
1268 }
1269
1270 /* Breakpoint manipulation */
1271
1272 /* Convert debugger type/mode into a core breakpoint type (core
1273 breakpoints have only a single access type, for efficiency, so a
1274 single TilemDebugBreakpoint may correspond to up to 3 core
1275 breakpoints) */
get_core_bp_type(int type,int mode)1276 static int get_core_bp_type(int type, int mode)
1277 {
1278 switch (type) {
1279 case TILEM_DB_BREAK_LOGICAL:
1280 switch (mode) {
1281 case TILEM_DB_BREAK_READ:
1282 return TILEM_BREAK_MEM_READ;
1283 case TILEM_DB_BREAK_WRITE:
1284 return TILEM_BREAK_MEM_WRITE;
1285 case TILEM_DB_BREAK_EXEC:
1286 return TILEM_BREAK_MEM_EXEC;
1287 }
1288 break;
1289
1290 case TILEM_DB_BREAK_PHYSICAL:
1291 switch (mode) {
1292 case TILEM_DB_BREAK_READ:
1293 return TILEM_BREAK_MEM_READ | TILEM_BREAK_PHYSICAL;
1294 case TILEM_DB_BREAK_WRITE:
1295 return TILEM_BREAK_MEM_WRITE | TILEM_BREAK_PHYSICAL;
1296 case TILEM_DB_BREAK_EXEC:
1297 return TILEM_BREAK_MEM_EXEC | TILEM_BREAK_PHYSICAL;
1298 }
1299 break;
1300
1301 case TILEM_DB_BREAK_PORT:
1302 switch (mode) {
1303 case TILEM_DB_BREAK_READ:
1304 return TILEM_BREAK_PORT_READ;
1305 case TILEM_DB_BREAK_WRITE:
1306 return TILEM_BREAK_PORT_WRITE;
1307 }
1308 break;
1309
1310 case TILEM_DB_BREAK_OPCODE:
1311 return TILEM_BREAK_EXECUTE;
1312 }
1313
1314 g_return_val_if_reached(0);
1315 }
1316
1317 /* Install core breakpoint(s) */
set_bp(TilemDebugger * dbg,TilemDebugBreakpoint * bp)1318 static void set_bp(TilemDebugger *dbg, TilemDebugBreakpoint *bp)
1319 {
1320 int i, t, n;
1321
1322 tilem_calc_emulator_lock(dbg->emu);
1323 for (i = 0; i < 3; i++) {
1324 if (!bp->disabled && (bp->mode & (1 << i))) {
1325 t = get_core_bp_type(bp->type, (1 << i));
1326 n = tilem_z80_add_breakpoint(dbg->emu->calc, t,
1327 bp->start, bp->end,
1328 bp->mask,
1329 NULL, NULL);
1330 bp->id[i] = n;
1331 }
1332 else {
1333 bp->id[i] = 0;
1334 }
1335 }
1336 tilem_calc_emulator_unlock(dbg->emu);
1337 }
1338
1339 /* Remove core breakpoint(s) */
unset_bp(TilemDebugger * dbg,TilemDebugBreakpoint * bp)1340 static void unset_bp(TilemDebugger *dbg, TilemDebugBreakpoint *bp)
1341 {
1342 int i;
1343 tilem_calc_emulator_lock(dbg->emu);
1344 for (i = 0; i < 3; i++) {
1345 if (bp->id[i])
1346 tilem_z80_remove_breakpoint(dbg->emu->calc, bp->id[i]);
1347 bp->id[i] = 0;
1348 }
1349 tilem_calc_emulator_unlock(dbg->emu);
1350 }
1351
is_mem_exec_bp(const TilemDebugBreakpoint * bp)1352 static gboolean is_mem_exec_bp(const TilemDebugBreakpoint *bp)
1353 {
1354 return ((bp->type == TILEM_DB_BREAK_LOGICAL
1355 || bp->type == TILEM_DB_BREAK_PHYSICAL)
1356 && (bp->mode & TILEM_DB_BREAK_EXEC));
1357 }
1358
1359 /* Add a new debugger breakpoint */
tilem_debugger_add_breakpoint(TilemDebugger * dbg,const TilemDebugBreakpoint * bp)1360 TilemDebugBreakpoint * tilem_debugger_add_breakpoint(TilemDebugger *dbg,
1361 const TilemDebugBreakpoint *bp)
1362 {
1363 TilemDebugBreakpoint *bp2;
1364
1365 g_return_val_if_fail(dbg != NULL, NULL);
1366 g_return_val_if_fail(bp != NULL, NULL);
1367 g_return_val_if_fail(bp->mode != 0, NULL);
1368
1369 bp2 = g_slice_new(TilemDebugBreakpoint);
1370 *bp2 = *bp;
1371 dbg->breakpoints = g_slist_append(dbg->breakpoints, bp2);
1372 set_bp(dbg, bp2);
1373
1374 if (is_mem_exec_bp(bp) && dbg->disasm_view)
1375 tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view));
1376
1377 return bp2;
1378 }
1379
1380 /* Remove a debugger breakpoint */
tilem_debugger_remove_breakpoint(TilemDebugger * dbg,TilemDebugBreakpoint * bp)1381 void tilem_debugger_remove_breakpoint(TilemDebugger *dbg,
1382 TilemDebugBreakpoint *bp)
1383 {
1384 gboolean isexec;
1385
1386 g_return_if_fail(dbg != NULL);
1387 g_return_if_fail(bp != NULL);
1388 g_return_if_fail(g_slist_index(dbg->breakpoints, bp) != -1);
1389
1390 isexec = is_mem_exec_bp(bp);
1391
1392 unset_bp(dbg, bp);
1393 dbg->breakpoints = g_slist_remove(dbg->breakpoints, bp);
1394 g_slice_free(TilemDebugBreakpoint, bp);
1395
1396 if (isexec && dbg->disasm_view)
1397 tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view));
1398 }
1399
1400 /* Modify a debugger breakpoint */
tilem_debugger_change_breakpoint(TilemDebugger * dbg,TilemDebugBreakpoint * bp,const TilemDebugBreakpoint * newbp)1401 void tilem_debugger_change_breakpoint(TilemDebugger *dbg,
1402 TilemDebugBreakpoint *bp,
1403 const TilemDebugBreakpoint *newbp)
1404 {
1405 gboolean isexec;
1406
1407 g_return_if_fail(dbg != NULL);
1408 g_return_if_fail(bp != NULL);
1409 g_return_if_fail(newbp != NULL);
1410 g_return_if_fail(g_slist_index(dbg->breakpoints, bp) != -1);
1411
1412 isexec = (is_mem_exec_bp(bp) || is_mem_exec_bp(newbp));
1413
1414 unset_bp(dbg, bp);
1415 *bp = *newbp;
1416 set_bp(dbg, bp);
1417
1418 if (isexec && dbg->disasm_view)
1419 tilem_disasm_view_refresh(TILEM_DISASM_VIEW(dbg->disasm_view));
1420 }
1421