1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <gdk/gdkkeysyms.h>
23
24 #include "kc/system.h"
25
26 #include "kc/z80.h"
27 #include "kc/memory.h"
28
29 #include "ui/gtk/cmd.h"
30 #include "ui/gtk/debug.h"
31
32 using namespace std;
33
34 extern "C" int disass(int, char **); /* FIXME */
35
36 class CMD_debug_window_toggle : public CMD
37 {
38 private:
39 DebugWindow *_w;
40
41 public:
CMD_debug_window_toggle(DebugWindow * w)42 CMD_debug_window_toggle(DebugWindow *w) : CMD("ui-debug-window-toggle")
43 {
44 _w = w;
45 register_cmd("ui-debug-window-toggle");
46 }
47
execute(CMD_Args * args,CMD_Context context)48 void execute(CMD_Args *args, CMD_Context context)
49 {
50 _w->toggle();
51 }
52 };
53
54 class CMD_single_step_executed : public CMD
55 {
56 private:
57 DebugWindow *_w;
58
59 public:
CMD_single_step_executed(DebugWindow * w)60 CMD_single_step_executed(DebugWindow *w) : CMD("single-step-executed")
61 {
62 _w = w;
63 register_cmd("single-step-executed");
64 }
65
execute(CMD_Args * args,CMD_Context context)66 void execute(CMD_Args *args, CMD_Context context)
67 {
68 _w->do_goto_int(z80->getPC());
69 }
70 };
71
72 void
do_goto_int(int addr)73 DebugWindow::do_goto_int(int addr)
74 {
75 _op->go_to(this, addr);
76 _op->update(this);
77 (new debug_op_reg())->update(this);
78 }
79
80 void
do_goto_string(const char * str)81 DebugWindow::do_goto_string(const char *str)
82 {
83 long val;
84 char *endptr;
85
86 if (str != NULL)
87 {
88 val = strtol(str, &endptr, 16);
89 if ((*str != '\0') && (*endptr == '\0'))
90 if ((val >= 0) && (val <= 0xffff))
91 {
92 do_goto_int(val);
93 return;
94 }
95 }
96
97 gdk_beep();
98 gtk_entry_select_region(GTK_ENTRY(_w.op), 0, -1);
99 }
100
101 gint
key_press_func(GtkWidget * widget,GdkEventKey * event,gpointer data)102 DebugWindow::key_press_func(GtkWidget *widget, GdkEventKey *event,
103 gpointer data)
104 {
105 DebugWindow *self;
106
107 int clear_op = 0;
108 self = (DebugWindow *)data;
109 if (event->state & GDK_CONTROL_MASK)
110 {
111 switch (event->keyval)
112 {
113 case 'd':
114 delete self->_op;
115 self->_op = new debug_op_asm();
116 self->_op->activate(self);
117 break;
118 case 'm':
119 delete self->_op;
120 self->_op = new debug_op_mem();
121 self->_op->activate(self);
122 break;
123 case 'r':
124 delete self->_op;
125 self->_op = new debug_op_reg();
126 self->_op->activate(self);
127 break;
128 case 'g':
129 /* gtk_label_set_text(GTK_LABEL(self->_w.op), "go to: "); */
130 if (self->_op->can_go_to())
131 {
132 // gtk_widget_show(self->_w.op);
133 self->_minibuffer_active = true;
134 gtk_widget_set_sensitive(self->_w.op, true);
135 gtk_entry_select_region(GTK_ENTRY(self->_w.op), 0, -1);
136 gtk_widget_grab_focus(self->_w.op);
137 }
138 break;
139 case 'p':
140 case 'b':
141 case GDK_Up:
142 case GDK_KP_Up:
143 self->_op->update(self, SCROLL_PREV_PAGE);
144 break;
145 case 'f':
146 case 'n':
147 case GDK_Down:
148 case GDK_KP_Down:
149 self->_op->update(self, SCROLL_NEXT_PAGE);
150 break;
151 }
152 }
153 else
154 {
155 switch (event->keyval)
156 {
157 case GDK_Return:
158 case GDK_KP_Enter:
159 if (!GTK_WIDGET_VISIBLE(self->_w.op)) break;
160 self->do_goto_string(gtk_entry_get_text(GTK_ENTRY(self->_w.op)));
161 break;
162 case GDK_Escape:
163 clear_op = 1;
164 break;
165 case GDK_BackSpace:
166 break;
167 case GDK_Up:
168 case GDK_KP_Up:
169 self->_op->update(self, SCROLL_BACKWARD);
170 break;
171 case GDK_Down:
172 case GDK_KP_Down:
173 self->_op->update(self, SCROLL_FORWARD);
174 break;
175 case GDK_Page_Up:
176 case GDK_KP_Page_Up:
177 self->_op->update(self, SCROLL_PREV_PAGE);
178 break;
179 case GDK_Page_Down:
180 case GDK_KP_Page_Down:
181 self->_op->update(self, SCROLL_NEXT_PAGE);
182 break;
183 }
184 }
185
186 if (clear_op)
187 {
188 /* gtk_label_set_text(GTK_LABEL(self->_w.op), ""); */
189 // gtk_widget_hide(self->_w.op);
190 gtk_widget_set_sensitive(self->_w.op, false);
191 self->_op->update(self);
192 }
193
194 if (!self->_minibuffer_active)
195 gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
196
197 return FALSE;
198 }
199
DebugWindow(void)200 DebugWindow::DebugWindow(void)
201 {
202 _pc = 0xe000;
203 _mem = 0xe000;
204
205 _minibuffer_active = false;
206
207 _font_desc = pango_font_description_new();
208 pango_font_description_set_family(_font_desc, "Courier");
209 pango_font_description_set_style(_font_desc, PANGO_STYLE_NORMAL);
210 pango_font_description_set_variant(_font_desc, PANGO_VARIANT_NORMAL);
211 pango_font_description_set_weight(_font_desc, PANGO_WEIGHT_NORMAL);
212 pango_font_description_set_stretch(_font_desc, PANGO_STRETCH_NORMAL);
213 pango_font_description_set_size(_font_desc, 8 * PANGO_SCALE);
214
215 _cmd1 = new CMD_debug_window_toggle(this);
216 _cmd2 = new CMD_single_step_executed(this);
217 }
218
~DebugWindow(void)219 DebugWindow::~DebugWindow(void)
220 {
221 delete _cmd1;
222 delete _cmd2;
223 pango_font_description_free(_font_desc);
224 }
225
226 void
init(void)227 DebugWindow::init(void)
228 {
229 int a;
230 GdkCursor *cursor;
231
232 /*
233 * window
234 */
235 _window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
236 gtk_widget_set_name(_window, "DebugWindow");
237 gtk_window_set_title(GTK_WINDOW(_window), _("KCemu: Debugger"));
238 gtk_widget_set_uposition(_window, 650, 50);
239 gtk_signal_connect(GTK_OBJECT(_window), "delete_event",
240 GTK_SIGNAL_FUNC(cmd_exec_sft),
241 (char *)"ui-debug-window-toggle"); // FIXME:
242 gtk_signal_connect(GTK_OBJECT(_window), "key_press_event",
243 GTK_SIGNAL_FUNC(key_press_func), this);
244
245 /*
246 * vbox
247 */
248 _w.vbox = gtk_vbox_new(FALSE, 0);
249 gtk_container_add(GTK_CONTAINER(_window), _w.vbox);
250 gtk_widget_show(_w.vbox);
251
252 /*
253 * hbox
254 */
255 _w.hbox = gtk_hbox_new(TRUE, 0);
256 gtk_container_add(GTK_CONTAINER(_w.vbox), _w.hbox);
257 gtk_widget_show(_w.hbox);
258
259 /*
260 * active/ op label
261 */
262 _w.active = gtk_label_new("");
263 gtk_misc_set_alignment(GTK_MISC(_w.active), 0, 0.5);
264 gtk_box_pack_start(GTK_BOX(_w.hbox), _w.active, FALSE, TRUE, 0);
265 gtk_widget_show(_w.active);
266
267 /*
268 * asm eventbox
269 */
270 _w.evb_asm = gtk_event_box_new();
271 gtk_box_pack_start(GTK_BOX(_w.vbox), _w.evb_asm, FALSE, TRUE, 0);
272 gtk_widget_show(_w.evb_asm);
273 gtk_object_set_user_data(GTK_OBJECT(_w.evb_asm), this);
274 gtk_widget_realize(_w.evb_asm);
275 cursor = gdk_cursor_new(GDK_HAND1);
276 gdk_window_set_cursor(_w.evb_asm->window, cursor);
277 gdk_cursor_destroy(cursor);
278 gtk_widget_show(_w.evb_asm);
279
280 /*
281 * asm frame/ vbox
282 */
283 _w.frame_asm = gtk_frame_new(DEBUG_ASM_HEADLINE);
284 gtk_container_add(GTK_CONTAINER(_w.evb_asm), _w.frame_asm);
285 gtk_widget_show(_w.frame_asm);
286 _w.vbox_asm = gtk_vbox_new(FALSE, 0);
287 gtk_container_add(GTK_CONTAINER(_w.frame_asm), _w.vbox_asm);
288 gtk_widget_show(_w.vbox_asm);
289
290 /*
291 * mem eventbox
292 */
293 _w.evb_mem = gtk_event_box_new();
294 gtk_box_pack_start(GTK_BOX(_w.vbox), _w.evb_mem, FALSE, TRUE, 0);
295 gtk_widget_show(_w.evb_mem);
296 gtk_object_set_user_data(GTK_OBJECT(_w.evb_mem), this);
297 gtk_widget_realize(_w.evb_mem);
298 cursor = gdk_cursor_new(GDK_CROSSHAIR);
299 gdk_window_set_cursor(_w.evb_mem->window, cursor);
300 gdk_cursor_destroy(cursor);
301 gtk_widget_show(_w.evb_mem);
302
303 /*
304 * mem frame/ vbox
305 */
306 _w.frame_mem = gtk_frame_new(DEBUG_MEM_HEADLINE);
307 gtk_container_add(GTK_CONTAINER(_w.evb_mem), _w.frame_mem);
308 gtk_widget_show(_w.frame_mem);
309 _w.vbox_mem = gtk_vbox_new(FALSE, 0);
310 gtk_container_add(GTK_CONTAINER(_w.frame_mem), _w.vbox_mem);
311 gtk_widget_show(_w.vbox_mem);
312
313 /*
314 * reg eventbox
315 */
316 _w.evb_reg = gtk_event_box_new();
317 gtk_box_pack_start(GTK_BOX(_w.vbox), _w.evb_reg, FALSE, TRUE, 0);
318 gtk_widget_show(_w.evb_reg);
319 gtk_object_set_user_data(GTK_OBJECT(_w.evb_reg), this);
320 gtk_widget_realize(_w.evb_reg);
321 cursor = gdk_cursor_new(GDK_XTERM);
322 gdk_window_set_cursor(_w.evb_reg->window, cursor);
323 gdk_cursor_destroy(cursor);
324 gtk_widget_show(_w.evb_reg);
325
326 /*
327 * reg frame/ vbox
328 */
329 _w.frame_reg = gtk_frame_new(DEBUG_REG_HEADLINE);
330 gtk_container_add(GTK_CONTAINER(_w.evb_reg), _w.frame_reg);
331 gtk_widget_show(_w.frame_reg);
332 _w.vbox_reg = gtk_vbox_new(FALSE, 0);
333 gtk_container_add(GTK_CONTAINER(_w.frame_reg), _w.vbox_reg);
334 gtk_widget_show(_w.vbox_reg);
335
336 _w.trace = gtk_toggle_button_new_with_label(_("Trace"));
337 gtk_box_pack_start(GTK_BOX(_w.vbox), _w.trace, FALSE, TRUE, 0);
338 gtk_signal_connect(GTK_OBJECT(_w.trace), "clicked",
339 GTK_SIGNAL_FUNC(cmd_exec_sf),
340 (char *)"z80-trace-toggle"); // FIXME:
341 gtk_widget_show(_w.trace);
342
343 _w.single_step = gtk_toggle_button_new_with_label(_("Single Step"));
344 gtk_box_pack_start(GTK_BOX(_w.vbox), _w.single_step, FALSE, TRUE, 0);
345 gtk_signal_connect(GTK_OBJECT(_w.single_step), "clicked",
346 GTK_SIGNAL_FUNC(cmd_exec_sf),
347 (char *)"z80-single-step-toggle"); // FIXME:
348 gtk_widget_show(_w.single_step);
349
350 _w.execute_step = gtk_button_new_with_label(_("Execute Step"));
351 gtk_box_pack_start(GTK_BOX(_w.vbox), _w.execute_step, FALSE, TRUE, 0);
352 gtk_signal_connect(GTK_OBJECT(_w.execute_step), "clicked",
353 GTK_SIGNAL_FUNC(cmd_exec_sf),
354 (char *)"z80-execute-step"); // FIXME:
355 gtk_widget_show(_w.execute_step);
356
357 /*
358 _w.op = gtk_label_new("");
359 gtk_misc_set_alignment(GTK_MISC(_w.op), 0, 0.5);
360 */
361 _w.op = gtk_entry_new();
362 gtk_widget_set_sensitive(_w.op, false);
363 gtk_box_pack_start(GTK_BOX(_w.vbox), _w.op, FALSE, TRUE, 0);
364 gtk_widget_show(_w.op);
365
366 /*
367 *---------------------------------------------------------------------------
368 */
369
370 /*
371 * labels
372 */
373 for (a = 0;a < DEBUG_NR_OF_ASM_LABELS;a++)
374 {
375 _w.l_asm[a] = gtk_label_new("");
376 gtk_widget_modify_font(_w.l_asm[a], _font_desc);
377 gtk_misc_set_alignment(GTK_MISC(_w.l_asm[a]), 0, 0.5);
378 gtk_box_pack_start(GTK_BOX(_w.vbox_asm), _w.l_asm[a], FALSE, TRUE, 0);
379 gtk_widget_show(_w.l_asm[a]);
380 }
381 for (a = 0;a < DEBUG_NR_OF_MEM_LABELS;a++)
382 {
383 _w.l_mem[a] = gtk_label_new("");
384 gtk_widget_modify_font(_w.l_mem[a], _font_desc);
385 gtk_misc_set_alignment(GTK_MISC(_w.l_mem[a]), 0, 0.5);
386 gtk_box_pack_start(GTK_BOX(_w.vbox_mem), _w.l_mem[a], FALSE, TRUE, 0);
387 gtk_widget_show(_w.l_mem[a]);
388 }
389 for (a = 0;a < DEBUG_NR_OF_REG_LABELS;a++)
390 {
391 _w.l_reg[a] = gtk_label_new("");
392 gtk_widget_modify_font(_w.l_reg[a], _font_desc);
393 gtk_misc_set_alignment(GTK_MISC(_w.l_reg[a]), 0, 0.5);
394 gtk_box_pack_start(GTK_BOX(_w.vbox_reg), _w.l_reg[a], FALSE, TRUE, 0);
395 gtk_widget_show(_w.l_reg[a]);
396 }
397
398 /*
399 * initial display
400 */
401 _op = new debug_op_reg();
402 _op->update(this);
403 delete _op;
404
405 _op = new debug_op_mem();
406 _op->update(this);
407 delete _op;
408
409 _op = new debug_op_asm();
410 _op->update(this);
411 _op->activate(this);
412 }
413
414 void
activate(DebugWindow * w)415 debug_op_t::activate(DebugWindow *w)
416 {
417 char buf[256];
418
419 sprintf(buf, "Active Window: %s", get_name());
420 gtk_label_set_text(GTK_LABEL(w->_w.active), buf);
421 }
422
423 void
go_to(DebugWindow * w,int addr)424 debug_op_asm::go_to(DebugWindow *w, int addr)
425 {
426 w->_pc = addr;
427 }
428
429 void
update(DebugWindow * w,scroll_dir_t direction)430 debug_op_asm::update(DebugWindow *w, scroll_dir_t direction)
431 {
432 int a;
433 int done;
434 char *ptr;
435 int pc, newpc;
436
437 if (direction == SCROLL_NEXT_PAGE)
438 {
439 direction = SCROLL_NONE;
440 w->_pc = w->_pc_np;
441 }
442
443 newpc = w->_pc - 20;
444
445 a = 0;
446 done = 0;
447 do
448 {
449 pc = newpc;
450 newpc = 0; // FIXME: call disassembler here
451 free(ptr);
452 switch (direction)
453 {
454 case SCROLL_NONE:
455 done = 1;
456 break;
457 case SCROLL_FORWARD:
458 if ((pc <= w->_pc) && (newpc > w->_pc))
459 {
460 w->_pc = newpc;
461 done = 1;
462 }
463 break;
464 case SCROLL_BACKWARD:
465 if ((pc < w->_pc) && (newpc >= w->_pc))
466 {
467 w->_pc = pc;
468 done = 1;
469 }
470 break;
471 default:
472 break;
473 }
474 if (++a > 22)
475 {
476 cerr << "ARGH! (" __FILE__ ":" << __LINE__ << ")" << endl;
477 return;
478 }
479 }
480 while (!done);
481
482 pc = w->_pc;
483 for (a = 0;a < DEBUG_NR_OF_ASM_LABELS;a++)
484 {
485 if (a == (DEBUG_NR_OF_ASM_LABELS - 2))
486 w->_pc_np = pc;
487 pc = 0; // FIXME: call disassembler here
488 gtk_label_set_text(GTK_LABEL(w->_w.l_asm[a]), ptr);
489 free(ptr);
490 }
491
492 gtk_widget_queue_draw(w->_w.vbox_asm);
493 }
494
495 void
go_to(DebugWindow * w,int addr)496 debug_op_mem::go_to(DebugWindow *w, int addr)
497 {
498 w->_mem = addr;
499 }
500
501 void
update(DebugWindow * w,scroll_dir_t direction)502 debug_op_mem::update(DebugWindow *w, scroll_dir_t direction)
503 {
504 int c;
505 int a, b, mem;
506 char mem_str[100];
507
508 switch (direction)
509 {
510 case SCROLL_NONE:
511 break;
512 case SCROLL_FORWARD:
513 w->_mem += 16;
514 break;
515 case SCROLL_BACKWARD:
516 w->_mem -= 16;
517 break;
518 case SCROLL_NEXT_PAGE:
519 w->_mem += 16 * DEBUG_NR_OF_MEM_LABELS;
520 break;
521 case SCROLL_PREV_PAGE:
522 w->_mem -= 16 * DEBUG_NR_OF_MEM_LABELS;
523 break;
524 }
525
526 mem = w->_mem;
527 for (a = 0;a < DEBUG_NR_OF_MEM_LABELS;a++)
528 {
529 sprintf(mem_str, "%04xh:", mem);
530 for (b = 0;b < 16;b++)
531 {
532 sprintf(strchr(mem_str, 0), " %02x", memory->memRead8(mem + b));
533 if (b == 7) sprintf(strchr(mem_str, 0), " -");
534 }
535 sprintf(strchr(mem_str, 0), " | ");
536 for (b = 0;b < 16;b++)
537 {
538 c = memory->memRead8(mem + b);
539 sprintf(strchr(mem_str, 0), "%c", ((c >= 0x20) && (c < 0x80)) ? c : '.');
540 if (b == 7) sprintf(strchr(mem_str, 0), "-");
541 }
542 mem += 16;
543 gtk_label_set_text(GTK_LABEL(w->_w.l_mem[a]), mem_str);
544 }
545 }
546
547 void
update(DebugWindow * w,scroll_dir_t direction)548 debug_op_reg::update(DebugWindow *w, scroll_dir_t direction)
549 {
550 int a;
551 char buf[256];
552
553 a = 0;
554 sprintf(buf, "PC = %04xh SP = %04xh",
555 z80->getPC(),
556 z80->getSP());
557 gtk_label_set_text(GTK_LABEL(w->_w.l_reg[a++]), buf);
558 sprintf(buf, "AF = %04xh BC = %04xh DE = %04xh HL = %04xh",
559 z80->getAF(),
560 z80->getBC(),
561 z80->getDE(),
562 z80->getHL());
563 gtk_label_set_text(GTK_LABEL(w->_w.l_reg[a++]), buf);
564 sprintf(buf, "AF' = %04xh BC' = %04xh DE' = %04xh HL' = %04xh",
565 z80->getAFs(),
566 z80->getBCs(),
567 z80->getDEs(),
568 z80->getHLs());
569 gtk_label_set_text(GTK_LABEL(w->_w.l_reg[a++]), buf);
570 sprintf(buf, "IX = %04xh IY = %04xh IFF = %02xh I = %02xh",
571 z80->getIX(),
572 z80->getIY(),
573 z80->getIFF(),
574 z80->getI());
575 gtk_label_set_text(GTK_LABEL(w->_w.l_reg[a++]), buf);
576 gtk_widget_queue_draw(w->_w.vbox_reg);
577 }
578