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