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