1 /* debugger.c: the GTK debugger
2    Copyright (c) 2002-2015 Philip Kendall
3    Copyright (c) 2013 Sergio Baldoví
4    Copyright (c) 2015 Stuart Brady
5    Copyright (c) 2016 BogDan Vatra
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License along
18    with this program; if not, write to the Free Software Foundation, Inc.,
19    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21    Author contact information:
22 
23    E-mail: philip-fuse@shadowmagic.org.uk
24 
25 */
26 
27 #include <config.h>
28 
29 #include <math.h>
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35 
36 #include <libspectrum.h>
37 
38 #include "debugger/debugger.h"
39 #include "event.h"
40 #include "fuse.h"
41 #include "gtkcompat.h"
42 #include "gtkinternals.h"
43 #include "machine.h"
44 #include "memory_pages.h"
45 #include "peripherals/ide/zxcf.h"
46 #include "peripherals/scld.h"
47 #include "peripherals/ula.h"
48 #include "settings.h"
49 #include "ui/ui.h"
50 #include "z80/z80.h"
51 #include "z80/z80_macros.h"
52 
53 /* The various debugger panes */
54 typedef enum debugger_pane {
55 
56   DEBUGGER_PANE_BEGIN = 1,	/* Start marker */
57 
58   DEBUGGER_PANE_REGISTERS = DEBUGGER_PANE_BEGIN,
59   DEBUGGER_PANE_MEMORYMAP,
60   DEBUGGER_PANE_BREAKPOINTS,
61   DEBUGGER_PANE_DISASSEMBLY,
62   DEBUGGER_PANE_STACK,
63   DEBUGGER_PANE_EVENTS,
64 
65   DEBUGGER_PANE_END		/* End marker */
66 
67 } debugger_pane;
68 
69 /* The columns used in the breakpoints pane */
70 
71 enum {
72   BREAKPOINTS_COLUMN_ID,
73   BREAKPOINTS_COLUMN_TYPE,
74   BREAKPOINTS_COLUMN_VALUE,
75   BREAKPOINTS_COLUMN_IGNORE,
76   BREAKPOINTS_COLUMN_LIFE,
77   BREAKPOINTS_COLUMN_CONDITION,
78 
79   BREAKPOINTS_COLUMN_COUNT
80 };
81 
82 /* The columns used in the disassembly pane */
83 
84 enum {
85   DISASSEMBLY_COLUMN_ADDRESS,
86   DISASSEMBLY_COLUMN_INSTRUCTION,
87 
88   DISASSEMBLY_COLUMN_COUNT
89 };
90 
91 /* The columns used in the stack pane */
92 
93 enum {
94   STACK_COLUMN_ADDRESS,
95   STACK_COLUMN_VALUE_TEXT,
96   STACK_COLUMN_VALUE_INT,
97 
98   STACK_COLUMN_COUNT
99 };
100 
101 /* The columns used in the events pane */
102 
103 enum {
104   EVENTS_COLUMN_TIME,
105   EVENTS_COLUMN_TYPE,
106 
107   EVENTS_COLUMN_COUNT
108 };
109 
110 static int create_dialog( void );
111 static int hide_hidden_panes( void );
112 static GtkCheckMenuItem* get_pane_menu_item( debugger_pane pane );
113 static GtkWidget* get_pane( debugger_pane pane );
114 static int create_menu_bar( GtkBox *parent, GtkAccelGroup **accel_group );
115 static void toggle_display( GtkToggleAction* action, debugger_pane pane );
116 static void toggle_display_registers( GtkToggleAction* action, gpointer data );
117 static void toggle_display_memory_map( GtkToggleAction* action, gpointer data );
118 static void toggle_display_breakpoints( GtkToggleAction* action,
119                                         gpointer data );
120 static void toggle_display_disassembly( GtkToggleAction* action,
121                                         gpointer data );
122 static void toggle_display_stack( GtkToggleAction* action, gpointer data );
123 static void toggle_display_events( GtkToggleAction* action, gpointer data );
124 static int create_register_display( GtkBox *parent, PangoFontDescription *font );
125 static int create_memory_map( GtkBox *parent );
126 static void create_breakpoints( GtkBox *parent );
127 static void create_disassembly( GtkBox *parent, PangoFontDescription *font );
128 static void create_stack_display( GtkBox *parent, PangoFontDescription *font );
129 static void stack_activate( GtkTreeView *tree_view, GtkTreePath *path,
130 			    GtkTreeViewColumn *column, gpointer user_data );
131 static void create_events( GtkBox *parent );
132 static void events_activate( GtkTreeView *tree_view, GtkTreePath *path,
133 			     GtkTreeViewColumn *column, gpointer user_data );
134 static int create_command_entry( GtkBox *parent, GtkAccelGroup *accel_group );
135 static int create_buttons( GtkDialog *parent, GtkAccelGroup *accel_group );
136 
137 static int activate_debugger( void );
138 static void update_memory_map( void );
139 static void update_breakpoints( void );
140 static void update_disassembly( void );
141 static void update_events( void );
142 static void add_event( gpointer data, gpointer user_data );
143 static int deactivate_debugger( void );
144 
145 static gboolean
146 disassembly_key_press( GtkTreeView *list, GdkEventKey *event,
147                        gpointer user_data );
148 static gboolean
149 disassembly_wheel_scroll( GtkTreeView *list GCC_UNUSED, GdkEvent *event,
150                           gpointer user_data );
151 
152 static void move_disassembly( GtkAdjustment *adjustment, gpointer user_data );
153 
154 static void evaluate_command( GtkWidget *widget, gpointer user_data );
155 static void gtkui_debugger_done_step( GtkWidget *widget, gpointer user_data );
156 static void gtkui_debugger_done_continue( GtkWidget *widget,
157 					  gpointer user_data );
158 static void gtkui_debugger_break( GtkWidget *widget, gpointer user_data );
159 static gboolean delete_dialog( GtkWidget *widget, GdkEvent *event,
160 			       gpointer user_data );
161 static void gtkui_debugger_done_close( GtkWidget *widget, gpointer user_data );
162 
163 static GtkWidget *dialog,		/* The debugger dialog box */
164   *continue_button, *break_button,	/* Two of its buttons */
165   *register_display,			/* The register display */
166   *registers[18],			/* Individual registers */
167   *memory_map,				/* The memory map display */
168   *memory_map_table,                    /* The table for the memory map */
169   *map_label[MEMORY_PAGES_IN_64K][4],   /* Labels in the memory map */
170   *breakpoints,				/* The breakpoint display */
171   *disassembly_box,			/* A box to hold the disassembly */
172   *disassembly,				/* The actual disassembly widget */
173   *stack,				/* The stack display */
174   *events;				/* The events display */
175 
176 static GtkListStore *breakpoints_model, *disassembly_model, *stack_model,
177   *events_model;
178 
179 static GtkAdjustment *disassembly_scrollbar_adjustment;
180 
181 /* The top line of the current disassembly */
182 static libspectrum_word disassembly_top;
183 
184 /* The next line below the current disassembly */
185 static libspectrum_word disassembly_bottom;
186 
187 /* Have we created the above yet? */
188 static int dialog_created = 0;
189 
190 /* Is the debugger window active (as opposed to the debugger itself)? */
191 static int debugger_active;
192 
193 /* The UIManager used to create the menu bar */
194 static GtkUIManager *ui_manager_debugger = NULL;
195 
196 /* The debugger's menu bar */
197 const gchar debugger_menu[] =
198 "<menubar name='DebuggerMenu'>"
199 "  <menu name='View' action='VIEW'>"
200 "    <menuitem name='Registers' action='VIEW_REGISTERS'/>"
201 "    <menuitem name='Memory Map' action='VIEW_MEMORY_MAP'/>"
202 "    <menuitem name='Breakpoints' action='VIEW_BREAKPOINTS'/>"
203 "    <menuitem name='Disassembly' action='VIEW_DISASSEMBLY'/>"
204 "    <menuitem name='Stack' action='VIEW_STACK'/>"
205 "    <menuitem name='Events' action='VIEW_EVENTS'/>"
206 "  </menu>"
207 "</menubar>";
208 
209 /* The debugger's menu actions */
210 static GtkActionEntry menu_data[] = {
211 
212   { "VIEW", NULL, "_View", NULL, NULL, NULL },
213 
214 };
215 
216 static GtkToggleActionEntry menu_toggles[] = {
217 
218   { "VIEW_REGISTERS", NULL, "_Registers", NULL, NULL, G_CALLBACK( toggle_display_registers ), TRUE },
219   { "VIEW_MEMORY_MAP", NULL, "_Memory Map", NULL, NULL, G_CALLBACK( toggle_display_memory_map ), TRUE },
220   { "VIEW_BREAKPOINTS", NULL, "_Breakpoints", NULL, NULL, G_CALLBACK( toggle_display_breakpoints ), TRUE },
221   { "VIEW_DISASSEMBLY", NULL, "_Disassembly", NULL, NULL, G_CALLBACK( toggle_display_disassembly ), TRUE },
222   { "VIEW_STACK", NULL, "_Stack", NULL, NULL, G_CALLBACK( toggle_display_stack ), TRUE },
223   { "VIEW_EVENTS", NULL, "_Events", NULL, NULL, G_CALLBACK( toggle_display_events ), TRUE },
224 
225 };
226 
227 static const char*
format_8_bit(void)228 format_8_bit( void )
229 {
230   return debugger_output_base == 10 ? "%3d" : "0x%02X";
231 }
232 
233 static const char*
format_16_bit(void)234 format_16_bit( void )
235 {
236   return debugger_output_base == 10 ? "%5d" : "0x%04X";
237 }
238 
239 int
ui_debugger_activate(void)240 ui_debugger_activate( void )
241 {
242   int error;
243 
244   fuse_emulation_pause();
245 
246   /* Create the dialog box if it doesn't already exist */
247   if( !dialog_created ) if( create_dialog() ) return 1;
248 
249   gtk_widget_show_all( dialog );
250   error = hide_hidden_panes(); if( error ) return error;
251 
252   gtk_widget_set_sensitive( continue_button, 1 );
253   gtk_widget_set_sensitive( break_button, 0 );
254   if( !debugger_active ) activate_debugger();
255 
256   return 0;
257 }
258 
259 void
ui_breakpoints_updated(void)260 ui_breakpoints_updated( void )
261 {
262   /* TODO: Refresh debugger list here */
263 }
264 
265 static int
hide_hidden_panes(void)266 hide_hidden_panes( void )
267 {
268   debugger_pane i;
269   GtkCheckMenuItem *checkitem; GtkWidget *pane;
270 
271   for( i = DEBUGGER_PANE_BEGIN; i < DEBUGGER_PANE_END; i++ ) {
272 
273     checkitem = get_pane_menu_item( i ); if( !checkitem ) return 1;
274 
275     if( gtk_check_menu_item_get_active( checkitem ) ) continue;
276 
277     pane = get_pane( i ); if( !pane ) return 1;
278 
279     gtk_widget_hide( pane );
280   }
281 
282   return 0;
283 }
284 
285 static GtkCheckMenuItem*
get_pane_menu_item(debugger_pane pane)286 get_pane_menu_item( debugger_pane pane )
287 {
288   const gchar *path;
289   GtkWidget *menu_item;
290 
291   path = NULL;
292 
293   switch( pane ) {
294   case DEBUGGER_PANE_REGISTERS: path = "/View/Registers"; break;
295   case DEBUGGER_PANE_MEMORYMAP: path = "/View/Memory Map"; break;
296   case DEBUGGER_PANE_BREAKPOINTS: path = "/View/Breakpoints"; break;
297   case DEBUGGER_PANE_DISASSEMBLY: path = "/View/Disassembly"; break;
298   case DEBUGGER_PANE_STACK: path = "/View/Stack"; break;
299   case DEBUGGER_PANE_EVENTS: path = "/View/Events"; break;
300 
301   case DEBUGGER_PANE_END: break;
302   }
303 
304   if( !path ) {
305     ui_error( UI_ERROR_ERROR, "unknown debugger pane %u", pane );
306     return NULL;
307   }
308 
309   gchar *full_path = g_strdup_printf( "/DebuggerMenu%s", path );
310   menu_item = gtk_ui_manager_get_widget( ui_manager_debugger, full_path );
311   g_free( full_path );
312 
313   if( !menu_item ) {
314     ui_error( UI_ERROR_ERROR, "couldn't get menu item '%s'",
315 	      path );
316     return NULL;
317   }
318 
319   return GTK_CHECK_MENU_ITEM( menu_item );
320 }
321 
322 static GtkWidget*
get_pane(debugger_pane pane)323 get_pane( debugger_pane pane )
324 {
325   switch( pane ) {
326   case DEBUGGER_PANE_REGISTERS: return register_display;
327   case DEBUGGER_PANE_MEMORYMAP: return memory_map;
328   case DEBUGGER_PANE_BREAKPOINTS: return breakpoints;
329   case DEBUGGER_PANE_DISASSEMBLY: return disassembly_box;
330   case DEBUGGER_PANE_STACK: return stack;
331   case DEBUGGER_PANE_EVENTS: return events;
332 
333   case DEBUGGER_PANE_END: break;
334   }
335 
336   ui_error( UI_ERROR_ERROR, "unknown debugger pane %u", pane );
337   return NULL;
338 }
339 
340 int
ui_debugger_deactivate(int interruptable)341 ui_debugger_deactivate( int interruptable )
342 {
343   if( debugger_active ) deactivate_debugger();
344 
345   if( dialog_created ) {
346     gtk_widget_set_sensitive( continue_button, !interruptable );
347     gtk_widget_set_sensitive( break_button,     interruptable );
348   }
349 
350   return 0;
351 }
352 
353 static int
create_dialog(void)354 create_dialog( void )
355 {
356   int error;
357   GtkWidget *hbox, *vbox, *hbox2, *content_area;
358   GtkAccelGroup *accel_group;
359 
360   PangoFontDescription *font;
361 
362   error = gtkui_get_monospaced_font( &font ); if( error ) return error;
363 
364   dialog = gtkstock_dialog_new( "Fuse - Debugger",
365 				G_CALLBACK( delete_dialog ) );
366   content_area = gtk_dialog_get_content_area( GTK_DIALOG( dialog ) );
367 
368   /* The menu bar */
369   error = create_menu_bar( GTK_BOX( content_area ), &accel_group );
370   if( error ) return error;
371 
372   /* Keyboard shortcuts */
373   gtk_window_add_accel_group( GTK_WINDOW( dialog ), accel_group );
374 
375   /* Some boxes to contain the things we want to display */
376   hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
377   gtk_box_pack_start( GTK_BOX( content_area ), hbox, TRUE, TRUE, 5 );
378 
379   vbox = gtk_box_new( GTK_ORIENTATION_VERTICAL, 5 );
380   gtk_box_pack_start( GTK_BOX( hbox ), vbox, TRUE, TRUE, 5 );
381 
382   hbox2 = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 5 );
383   gtk_box_pack_start( GTK_BOX( vbox ), hbox2, TRUE, TRUE, 0 );
384 
385   /* The main display areas */
386   error = create_register_display( GTK_BOX( hbox2 ), font );
387   if( error ) return error;
388 
389   error = create_memory_map( GTK_BOX( hbox2 ) ); if( error ) return error;
390 
391   create_breakpoints( GTK_BOX( vbox ) );
392   create_disassembly( GTK_BOX( hbox ), font );
393   create_stack_display( GTK_BOX( hbox ), font );
394   create_events( GTK_BOX( hbox ) );
395 
396   error = create_command_entry( GTK_BOX( content_area ), accel_group );
397   if( error ) return error;
398 
399   /* The action buttons */
400 
401   error = create_buttons( GTK_DIALOG( dialog ), accel_group );
402   if( error ) return error;
403 
404   gtkui_free_font( font );
405 
406   dialog_created = 1;
407 
408   return 0;
409 }
410 
411 static int
create_menu_bar(GtkBox * parent,GtkAccelGroup ** accel_group)412 create_menu_bar( GtkBox *parent, GtkAccelGroup **accel_group )
413 {
414   GError *error = NULL;
415   GtkActionGroup *menu_action_group;
416   GtkWidget *menu_bar;
417   guint ui_menu_id;
418 
419   /* FIXME: we should unref this at some point */
420   ui_manager_debugger = gtk_ui_manager_new();
421 
422   /* Load actions */
423   menu_action_group = gtk_action_group_new( "DebuggerActionGroup" );
424   gtk_action_group_add_actions( menu_action_group, menu_data,
425 				ARRAY_SIZE( menu_data ), NULL );
426   gtk_action_group_add_toggle_actions( menu_action_group, menu_toggles,
427                                        ARRAY_SIZE( menu_toggles ), NULL );
428   gtk_ui_manager_insert_action_group( ui_manager_debugger, menu_action_group,
429                                       0 );
430   g_object_unref( menu_action_group );
431 
432   /* Load the menu */
433   ui_menu_id = gtk_ui_manager_add_ui_from_string( ui_manager_debugger,
434                                                   debugger_menu,
435                                                   sizeof( debugger_menu ),
436                                                   &error );
437   if( error ) {
438     g_error_free( error );
439     return 1;
440   }
441   else if( !ui_menu_id ) return 1;
442 
443   *accel_group = gtk_ui_manager_get_accel_group( ui_manager_debugger );
444 
445   menu_bar = gtk_ui_manager_get_widget( ui_manager_debugger, "/DebuggerMenu" );
446 
447   gtk_box_pack_start( parent, menu_bar, FALSE, FALSE, 0 );
448 
449   return 0;
450 }
451 
452 static void
toggle_display(GtkToggleAction * action,debugger_pane pane_id)453 toggle_display( GtkToggleAction* action, debugger_pane pane_id )
454 {
455   GtkWidget *pane;
456 
457   pane = get_pane( pane_id ); if( !pane ) return;
458 
459   if( gtk_toggle_action_get_active( action ) ) {
460     gtk_widget_show_all( pane );
461   } else {
462     gtk_widget_hide( pane );
463   }
464 }
465 
466 static void
toggle_display_registers(GtkToggleAction * action,gpointer data GCC_UNUSED)467 toggle_display_registers( GtkToggleAction* action, gpointer data GCC_UNUSED )
468 {
469   toggle_display( action, DEBUGGER_PANE_REGISTERS );
470 }
471 
472 static void
toggle_display_memory_map(GtkToggleAction * action,gpointer data GCC_UNUSED)473 toggle_display_memory_map( GtkToggleAction* action, gpointer data GCC_UNUSED )
474 {
475   toggle_display( action, DEBUGGER_PANE_MEMORYMAP );
476 }
477 
478 static void
toggle_display_breakpoints(GtkToggleAction * action,gpointer data GCC_UNUSED)479 toggle_display_breakpoints( GtkToggleAction* action, gpointer data GCC_UNUSED )
480 {
481   toggle_display( action, DEBUGGER_PANE_BREAKPOINTS );
482 }
483 
484 static void
toggle_display_disassembly(GtkToggleAction * action,gpointer data GCC_UNUSED)485 toggle_display_disassembly( GtkToggleAction* action, gpointer data GCC_UNUSED )
486 {
487   toggle_display( action, DEBUGGER_PANE_DISASSEMBLY );
488 }
489 
490 static void
toggle_display_stack(GtkToggleAction * action,gpointer data GCC_UNUSED)491 toggle_display_stack( GtkToggleAction* action, gpointer data GCC_UNUSED )
492 {
493   toggle_display( action, DEBUGGER_PANE_STACK );
494 }
495 
496 static void
toggle_display_events(GtkToggleAction * action,gpointer data GCC_UNUSED)497 toggle_display_events( GtkToggleAction* action, gpointer data GCC_UNUSED )
498 {
499   toggle_display( action, DEBUGGER_PANE_EVENTS );
500 }
501 
502 static int
create_register_display(GtkBox * parent,PangoFontDescription * font)503 create_register_display( GtkBox *parent, PangoFontDescription *font )
504 {
505   size_t i;
506 
507 #if GTK_CHECK_VERSION( 3, 0, 0 )
508 
509   register_display = gtk_grid_new();
510   gtk_grid_set_row_spacing( GTK_GRID( register_display ), 4 );
511   gtk_container_set_border_width( GTK_CONTAINER( register_display ), 6 );
512 
513 #else                /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
514 
515   register_display = gtk_table_new( 9, 2, FALSE );
516 
517 #endif
518 
519   gtk_box_pack_start( parent, register_display, FALSE, FALSE, 0 );
520 
521   for( i = 0; i < 18; i++ ) {
522     PangoAttrList *list;
523     PangoAttribute *attr;
524 
525     registers[i] = gtk_label_new( "" );
526 
527     list = pango_attr_list_new();
528     attr = pango_attr_font_desc_new( font );
529     pango_attr_list_insert( list, attr );
530     gtk_label_set_attributes( GTK_LABEL( registers[i] ), list );
531     pango_attr_list_unref( list );
532 
533 #if GTK_CHECK_VERSION( 3, 0, 0 )
534     gtk_grid_attach( GTK_GRID( register_display ), registers[i],
535                      i%2, i/2, 1, 1 );
536 #else
537     gtk_table_attach( GTK_TABLE( register_display ), registers[i],
538                       i%2, i%2+1, i/2, i/2+1, 0, 0, 2, 2 );
539 #endif
540 
541   }
542 
543   return 0;
544 }
545 
546 static int
create_memory_map(GtkBox * parent)547 create_memory_map( GtkBox *parent )
548 {
549   GtkWidget *label_address, *label_source, *label_writable, *label_contended;
550 
551   label_address   = gtk_label_new( "Address" );
552   label_source    = gtk_label_new( "Source" );
553   label_writable  = gtk_label_new( "W?" );
554   label_contended = gtk_label_new( "C?" );
555 
556   memory_map = gtk_frame_new( "Memory Map" );
557   gtk_box_pack_start( parent, memory_map, FALSE, FALSE, 0 );
558 
559 #if GTK_CHECK_VERSION( 3, 0, 0 )
560 
561   memory_map_table = gtk_grid_new();
562   gtk_grid_set_row_spacing( GTK_GRID( memory_map_table ), 4 );
563   gtk_grid_set_column_spacing( GTK_GRID( memory_map_table ), 6 );
564   gtk_container_set_border_width( GTK_CONTAINER( memory_map_table ), 6 );
565   gtk_container_add( GTK_CONTAINER( memory_map ), memory_map_table );
566 
567   gtk_grid_attach( GTK_GRID( memory_map_table ), label_address, 0, 0, 1, 1 );
568   gtk_grid_attach( GTK_GRID( memory_map_table ), label_source, 1, 0, 1, 1 );
569   gtk_grid_attach( GTK_GRID( memory_map_table ), label_writable, 2, 0, 1, 1 );
570   gtk_grid_attach( GTK_GRID( memory_map_table ), label_contended, 3, 0, 1, 1 );
571 
572 #else                /* #if GTK_CHECK_VERSION( 3, 0, 0 ) */
573 
574   memory_map_table = gtk_table_new( 1 + MEMORY_PAGES_IN_64K, 4, FALSE );
575   gtk_container_add( GTK_CONTAINER( memory_map ), memory_map_table );
576 
577   gtk_table_attach( GTK_TABLE( memory_map_table ), label_address,
578                     0, 1, 0, 1, 0, 0, 2, 2 );
579   gtk_table_attach( GTK_TABLE( memory_map_table ), label_source,
580                     1, 2, 0, 1, 0, 0, 2, 2 );
581   gtk_table_attach( GTK_TABLE( memory_map_table ), label_writable,
582                     2, 3, 0, 1, 0, 0, 2, 2 );
583   gtk_table_attach( GTK_TABLE( memory_map_table ), label_contended,
584                     3, 4, 0, 1, 0, 0, 2, 2 );
585 
586 #endif
587 
588   return 0;
589 }
590 
591 static void
create_breakpoints(GtkBox * parent)592 create_breakpoints( GtkBox *parent )
593 {
594   size_t i;
595 
596   static const gchar *const titles[] =
597     { "ID", "Type", "Value", "Ignore", "Life", "Condition" };
598 
599   breakpoints_model = gtk_list_store_new( BREAKPOINTS_COLUMN_COUNT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING );
600 
601   breakpoints = gtk_tree_view_new_with_model( GTK_TREE_MODEL( breakpoints_model ) );
602   for( i = 0; i < BREAKPOINTS_COLUMN_COUNT; i++ ) {
603     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
604     GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( titles[i], renderer, "text", i, NULL );
605     gtk_tree_view_append_column( GTK_TREE_VIEW( breakpoints ), column );
606   }
607 
608   gtk_box_pack_start( parent, breakpoints, TRUE, TRUE, 0 );
609 }
610 
611 static void
create_disassembly(GtkBox * parent,PangoFontDescription * font)612 create_disassembly( GtkBox *parent, PangoFontDescription *font )
613 {
614   size_t i;
615 
616   GtkWidget *scrollbar;
617   static const gchar *const titles[] =
618     { "Address", "Instruction" };
619 
620   /* A box to hold the disassembly listing and the scrollbar */
621   disassembly_box = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
622   gtk_box_pack_start( parent, disassembly_box, TRUE, TRUE, 0 );
623 
624   /* The disassembly itself */
625   disassembly_model =
626     gtk_list_store_new( DISASSEMBLY_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_STRING );
627 
628   disassembly = gtk_tree_view_new_with_model( GTK_TREE_MODEL( disassembly_model ) );
629   for( i = 0; i < DISASSEMBLY_COLUMN_COUNT; i++ ) {
630     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
631     GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( titles[i], renderer, "text", i, NULL );
632     g_object_set( G_OBJECT( renderer ), "font-desc", font, "height", 18, NULL );
633     gtk_tree_view_append_column( GTK_TREE_VIEW( disassembly ), column );
634   }
635 
636   gtk_box_pack_start( GTK_BOX( disassembly_box ), disassembly, TRUE, TRUE, 0 );
637 
638   /* The disassembly scrollbar */
639   disassembly_scrollbar_adjustment = GTK_ADJUSTMENT(
640     gtk_adjustment_new( 0, 0x0000, 0x10000, 1, 20, 20 ) );
641   g_signal_connect( G_OBJECT( disassembly_scrollbar_adjustment ),
642 		    "value-changed", G_CALLBACK( move_disassembly ),
643 		    NULL );
644   scrollbar = gtk_scrollbar_new( GTK_ORIENTATION_VERTICAL,
645                                  disassembly_scrollbar_adjustment );
646   gtk_box_pack_start( GTK_BOX( disassembly_box ), scrollbar, FALSE, FALSE, 0 );
647 
648   /* Scrolling with keys */
649   g_signal_connect( GTK_TREE_VIEW( disassembly ), "key-press-event",
650                     G_CALLBACK( disassembly_key_press ),
651                     disassembly_scrollbar_adjustment );
652 
653   /* Scrolling with mouse wheel */
654   g_signal_connect( GTK_TREE_VIEW( disassembly ), "scroll-event",
655                     G_CALLBACK( disassembly_wheel_scroll ),
656                     disassembly_scrollbar_adjustment );
657 }
658 
659 static void
create_stack_display(GtkBox * parent,PangoFontDescription * font)660 create_stack_display( GtkBox *parent, PangoFontDescription *font )
661 {
662   size_t i;
663   static const gchar *const titles[] = { "Address", "Instruction" };
664 
665   stack_model =
666     gtk_list_store_new( STACK_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_STRING,
667 			G_TYPE_INT );
668 
669   stack = gtk_tree_view_new_with_model( GTK_TREE_MODEL( stack_model ) );
670 
671   for( i = 0; i < 2; i++ ) {
672     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
673     GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( titles[i], renderer, "text", i, NULL );
674     g_object_set( G_OBJECT( renderer ), "font-desc", font, "height", 18, NULL );
675     gtk_tree_view_append_column( GTK_TREE_VIEW( stack ), column );
676   }
677 
678   gtk_box_pack_start( parent, stack, TRUE, TRUE, 5 );
679 
680   g_signal_connect( G_OBJECT( stack ), "row-activated", G_CALLBACK( stack_activate ), NULL );
681 }
682 
683 static void
stack_activate(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column GCC_UNUSED,gpointer user_data GCC_UNUSED)684 stack_activate( GtkTreeView *tree_view, GtkTreePath *path,
685 	        GtkTreeViewColumn *column GCC_UNUSED,
686 		gpointer user_data GCC_UNUSED )
687 {
688   GtkTreeIter it;
689   GtkTreeModel *model = gtk_tree_view_get_model( tree_view );
690 
691   if( model && gtk_tree_model_get_iter( model, &it, path ) ) {
692     gint address;
693     int error;
694 
695     gtk_tree_model_get( model, &it, STACK_COLUMN_VALUE_INT, &address, -1 );
696 
697     error = debugger_breakpoint_add_address(
698       DEBUGGER_BREAKPOINT_TYPE_EXECUTE, memory_source_any, 0, address, 0,
699       DEBUGGER_BREAKPOINT_LIFE_ONESHOT, NULL
700     );
701     if( error ) return;
702 
703     debugger_run();
704   }
705 }
706 
707 static void
create_events(GtkBox * parent)708 create_events( GtkBox *parent )
709 {
710   static const gchar *const titles[] = { "Time", "Type" };
711   size_t i;
712 
713   events_model =
714     gtk_list_store_new( EVENTS_COLUMN_COUNT, G_TYPE_INT, G_TYPE_STRING );
715 
716   events = gtk_tree_view_new_with_model( GTK_TREE_MODEL( events_model ) );
717 
718   for( i = 0; i < EVENTS_COLUMN_COUNT; i++ ) {
719     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
720     GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes( titles[i], renderer, "text", i, NULL );
721     gtk_tree_view_append_column( GTK_TREE_VIEW( events ), column );
722   }
723 
724   gtk_box_pack_start( parent, events, TRUE, TRUE, 5 );
725 
726   g_signal_connect( G_OBJECT( events ), "row-activated", G_CALLBACK( events_activate ), NULL );
727 }
728 
729 static void
events_activate(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column GCC_UNUSED,gpointer user_data GCC_UNUSED)730 events_activate( GtkTreeView *tree_view, GtkTreePath *path,
731 	         GtkTreeViewColumn *column GCC_UNUSED,
732 		 gpointer user_data GCC_UNUSED )
733 {
734   GtkTreeIter it;
735   GtkTreeModel *model = gtk_tree_view_get_model( tree_view );
736 
737   if( model && gtk_tree_model_get_iter( model, &it, path ) ) {
738     libspectrum_dword event_tstates;
739     int error;
740 
741     gtk_tree_model_get( model, &it, EVENTS_COLUMN_TIME, &event_tstates, -1 );
742 
743     error = debugger_breakpoint_add_time(
744       DEBUGGER_BREAKPOINT_TYPE_TIME, event_tstates, 0,
745       DEBUGGER_BREAKPOINT_LIFE_ONESHOT, NULL
746     );
747     if( error ) return;
748 
749     debugger_run();
750   }
751 }
752 
753 static int
create_command_entry(GtkBox * parent,GtkAccelGroup * accel_group)754 create_command_entry( GtkBox *parent, GtkAccelGroup *accel_group )
755 {
756   GtkWidget *hbox, *entry, *eval_button;
757 
758   /* An hbox to hold the command entry widget and the 'evaluate' button */
759   hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 5 );
760   gtk_box_pack_start( parent, hbox, FALSE, FALSE, 0 );
761 
762   /* The command entry widget */
763   entry = gtk_entry_new();
764   g_signal_connect( G_OBJECT( entry ), "activate",
765 		    G_CALLBACK( evaluate_command ), NULL );
766   gtk_box_pack_start( GTK_BOX( hbox ), entry, TRUE, TRUE, 0 );
767 
768   /* The 'command evaluate' button */
769   eval_button = gtk_button_new_with_label( "Evaluate" );
770   g_signal_connect_swapped( G_OBJECT( eval_button ), "clicked",
771 			    G_CALLBACK( evaluate_command ),
772 			    G_OBJECT( entry ) );
773   gtk_box_pack_start( GTK_BOX( hbox ), eval_button, FALSE, FALSE, 0 );
774 
775   /* Return is equivalent to clicking on 'evaluate' */
776   gtk_widget_add_accelerator( eval_button, "clicked", accel_group,
777 			      GDK_KEY_Return, 0, 0 );
778 
779   return 0;
780 }
781 
782 static int
create_buttons(GtkDialog * parent,GtkAccelGroup * accel_group)783 create_buttons( GtkDialog *parent, GtkAccelGroup *accel_group )
784 {
785   static const gtkstock_button
786     step  = { "Single step", G_CALLBACK( gtkui_debugger_done_step ), NULL, NULL, 0, 0, 0, 0, GTK_RESPONSE_NONE },
787     cont  = { "Continue", G_CALLBACK( gtkui_debugger_done_continue ), NULL, NULL, 0, 0, 0, 0, GTK_RESPONSE_NONE },
788     brk   = { "Break", G_CALLBACK( gtkui_debugger_break ), NULL, NULL, 0, 0, 0, 0, GTK_RESPONSE_NONE };
789 
790   /* Create the action buttons for the dialog box */
791   gtkstock_create_button( GTK_WIDGET( parent ), accel_group, &step );
792   continue_button = gtkstock_create_button( GTK_WIDGET( parent ), accel_group,
793 					    &cont );
794   break_button = gtkstock_create_button( GTK_WIDGET( parent ), accel_group,
795 					 &brk );
796   gtkstock_create_close( GTK_WIDGET( parent ), accel_group,
797 			 G_CALLBACK( gtkui_debugger_done_close ), TRUE );
798 
799   return 0;
800 }
801 
802 static int
activate_debugger(void)803 activate_debugger( void )
804 {
805   debugger_active = 1;
806 
807   ui_debugger_disassemble( PC );
808   ui_debugger_update();
809 
810   gtk_main();
811   return 0;
812 }
813 
814 /* Update the debugger's display */
815 int
ui_debugger_update(void)816 ui_debugger_update( void )
817 {
818   size_t i;
819   char buffer[1024], format_string[1024];
820   gchar buffer1[80], buffer2[80];
821   libspectrum_word address;
822   int capabilities; size_t length;
823 
824   const char *register_name[] = { "PC", "SP",
825 				  "AF", "AF'",
826 				  "BC", "BC'",
827 				  "DE", "DE'",
828 				  "HL", "HL'",
829 				  "IX", "IY",
830                                 };
831 
832   libspectrum_word *value_ptr[] = { &PC, &SP,  &AF, &AF_,
833 				    &BC, &BC_, &DE, &DE_,
834 				    &HL, &HL_, &IX, &IY,
835 				  };
836 
837   if( !dialog_created ) return 0;
838 
839   for( i = 0; i < 12; i++ ) {
840     snprintf( buffer, 5, "%3s ", register_name[i] );
841     snprintf( &buffer[4], 76, format_16_bit(), *value_ptr[i] );
842     gtk_label_set_text( GTK_LABEL( registers[i] ), buffer );
843   }
844 
845   strcpy( buffer, "  I   " ); snprintf( &buffer[6], 76, format_8_bit(), I );
846   gtk_label_set_text( GTK_LABEL( registers[12] ), buffer );
847   strcpy( buffer, "  R   " );
848   snprintf( &buffer[6], 80, format_8_bit(), ( R & 0x7f ) | ( R7 & 0x80 ) );
849   gtk_label_set_text( GTK_LABEL( registers[13] ), buffer );
850 
851   snprintf( buffer, 80, "T-states %5d\nHalted %d", tstates, z80.halted );
852   gtk_label_set_text( GTK_LABEL( registers[14] ), buffer );
853   snprintf( buffer, 80, "  IM %d\nIFF1 %d\nIFF2 %d", IM, IFF1, IFF2 );
854   gtk_label_set_text( GTK_LABEL( registers[15] ), buffer );
855 
856   strcpy( buffer, "SZ5H3PNC\n" );
857   for( i = 0; i < 8; i++ ) buffer[i+9] = ( F & ( 0x80 >> i ) ) ? '1' : '0';
858   buffer[17] = '\0';
859   gtk_label_set_text( GTK_LABEL( registers[16] ), buffer );
860 
861   capabilities = libspectrum_machine_capabilities( machine_current->machine );
862 
863   sprintf( format_string, "   ULA %s", format_8_bit() );
864   snprintf( buffer, 1024, format_string, ula_last_byte() );
865 
866   if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_AY ) {
867     sprintf( format_string, "\n    AY %s", format_8_bit() );
868     length = strlen( buffer );
869     snprintf( &buffer[length], 1024-length, format_string,
870 	      machine_current->ay.current_register );
871   }
872 
873   if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) {
874     sprintf( format_string, "\n128Mem %s", format_8_bit() );
875     length = strlen( buffer );
876     snprintf( &buffer[length], 1024-length, format_string,
877 	      machine_current->ram.last_byte );
878   }
879 
880   if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY ) {
881     sprintf( format_string, "\n+3 Mem %s", format_8_bit() );
882     length = strlen( buffer );
883     snprintf( &buffer[length], 1024-length, format_string,
884 	      machine_current->ram.last_byte2 );
885   }
886 
887   if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_VIDEO  ||
888       capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY ||
889       capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY       ) {
890     sprintf( format_string, "\nTmxDec %s", format_8_bit() );
891     length = strlen( buffer );
892     snprintf( &buffer[length], 1024-length, format_string,
893 	      scld_last_dec.byte );
894   }
895 
896   if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY ||
897       capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY       ) {
898     sprintf( format_string, "\nTmxHsr %s", format_8_bit() );
899     length = strlen( buffer );
900     snprintf( &buffer[length], 1024-length, format_string, scld_last_hsr );
901   }
902 
903   if( settings_current.zxcf_active ) {
904     sprintf( format_string, "\n  ZXCF %s", format_8_bit() );
905     length = strlen( buffer );
906     snprintf( &buffer[length], 1024-length, format_string,
907 	      zxcf_last_memctl() );
908   }
909 
910   gtk_label_set_text( GTK_LABEL( registers[17] ), buffer );
911 
912   update_memory_map();
913   update_breakpoints();
914   update_disassembly();
915 
916   /* And the stack display */
917   gtk_list_store_clear( stack_model );
918 
919   for( i = 0, address = SP + 38; i < 20; i++, address -= 2 ) {
920 
921     GtkTreeIter it;
922 
923     libspectrum_word contents = readbyte_internal( address ) +
924 				0x100 * readbyte_internal( address + 1 );
925 
926     snprintf( buffer1, sizeof( buffer1 ), format_16_bit(), address );
927     snprintf( buffer2, sizeof( buffer2 ), format_16_bit(), contents );
928 
929     gtk_list_store_append( stack_model, &it );
930     gtk_list_store_set( stack_model, &it, STACK_COLUMN_ADDRESS, buffer1, STACK_COLUMN_VALUE_TEXT, buffer2, STACK_COLUMN_VALUE_INT, (gint)contents, -1 );
931   }
932 
933   /* And the events display */
934   update_events();
935 
936   return 0;
937 }
938 
939 static void
update_memory_map(void)940 update_memory_map( void )
941 {
942   int source, page_num, writable, contended;
943   libspectrum_word offset;
944   size_t i, j, block, row;
945 
946   for( i = 0; i < MEMORY_PAGES_IN_64K; i++ ) {
947     if( map_label[i][0] ) {
948       for( j = 0; j < 4; j++ ) {
949         gtk_container_remove( GTK_CONTAINER( memory_map_table ), map_label[i][j] );
950         map_label[i][j] = NULL;
951       }
952     }
953   }
954 
955   source = page_num = writable = contended = -1;
956   offset = 0;
957   row = 0;
958 
959   for( block = 0; block < MEMORY_PAGES_IN_64K; block++ ) {
960     memory_page *page = &memory_map_read[block];
961 
962     if( page->source != source ||
963       page->page_num != page_num ||
964       page->offset != offset ||
965       page->writable != writable ||
966       page->contended != contended ) {
967 
968       char buffer[40];
969       GtkWidget **row_labels = map_label[row];
970 
971       snprintf( buffer, 40, format_16_bit(),
972                 (unsigned)block * MEMORY_PAGE_SIZE );
973       row_labels[0] = gtk_label_new( buffer );
974 
975       snprintf( buffer, 40, "%s %d",
976         memory_source_description( page->source ), page->page_num );
977       row_labels[1] = gtk_label_new( buffer );
978 
979       row_labels[2] = gtk_label_new( page->writable ? "Y" : "N" );
980       row_labels[3] = gtk_label_new( page->contended ? "Y" : "N" );
981 
982       for( i = 0; i < 4; i++ ) {
983 
984 #if GTK_CHECK_VERSION( 3, 0, 0 )
985         gtk_grid_attach( GTK_GRID( memory_map_table ), row_labels[i],
986                          i, row + 1, 1, 1 );
987 #else
988         gtk_table_attach( GTK_TABLE( memory_map_table ), row_labels[i],
989                           i, i + 1, row + 1, row + 2, 0, 0, 2, 2 );
990 #endif
991 
992       }
993 
994       row++;
995 
996       source = page->source;
997       page_num = page->page_num;
998       writable = page->writable;
999       contended = page->contended;
1000       offset = page->offset;
1001     }
1002 
1003     /* We expect the next page to have an increased offset */
1004     offset += MEMORY_PAGE_SIZE;
1005   }
1006 
1007   gtk_widget_show_all( GTK_WIDGET( memory_map_table ) );
1008 }
1009 
1010 static void
update_breakpoints(void)1011 update_breakpoints( void )
1012 {
1013   GSList *ptr;
1014 
1015   gtk_list_store_clear( breakpoints_model );
1016 
1017   for( ptr = debugger_breakpoints; ptr; ptr = ptr->next ) {
1018 
1019     debugger_breakpoint *bp = ptr->data;
1020     GtkTreeIter it;
1021     gchar buffer[40], format_string[40];
1022 
1023     switch( bp->type ) {
1024 
1025     case DEBUGGER_BREAKPOINT_TYPE_EXECUTE:
1026     case DEBUGGER_BREAKPOINT_TYPE_READ:
1027     case DEBUGGER_BREAKPOINT_TYPE_WRITE:
1028       if( bp->value.address.source == memory_source_any ) {
1029 	snprintf( buffer, sizeof( buffer ), format_16_bit(),
1030 		  bp->value.address.offset );
1031       } else {
1032 	snprintf( format_string, sizeof( format_string ), "%%s:%s:%s",
1033 		  format_16_bit(), format_16_bit() );
1034 	snprintf( buffer, sizeof( buffer ), format_string,
1035                   memory_source_description( bp->value.address.source ),
1036                   bp->value.address.page, bp->value.address.offset );
1037       }
1038       break;
1039 
1040     case DEBUGGER_BREAKPOINT_TYPE_PORT_READ:
1041     case DEBUGGER_BREAKPOINT_TYPE_PORT_WRITE:
1042       snprintf( format_string, sizeof( format_string ), "%s:%s",
1043 		format_16_bit(), format_16_bit() );
1044       snprintf( buffer, sizeof( buffer ), format_string, bp->value.port.mask,
1045 		bp->value.port.port );
1046       break;
1047 
1048     case DEBUGGER_BREAKPOINT_TYPE_TIME:
1049       snprintf( buffer, sizeof( buffer ), "%5d", bp->value.time.tstates );
1050       break;
1051 
1052     case DEBUGGER_BREAKPOINT_TYPE_EVENT:
1053       snprintf( buffer, sizeof( buffer ), "%s:%s", bp->value.event.type,
1054 		bp->value.event.detail );
1055       break;
1056 
1057     }
1058 
1059     gtk_list_store_append( breakpoints_model, &it );
1060     gtk_list_store_set(
1061       breakpoints_model, &it,
1062       BREAKPOINTS_COLUMN_ID, bp->id,
1063       BREAKPOINTS_COLUMN_TYPE, debugger_breakpoint_type_text[ bp->type ],
1064       BREAKPOINTS_COLUMN_VALUE, buffer,
1065       BREAKPOINTS_COLUMN_IGNORE, bp->ignore,
1066       BREAKPOINTS_COLUMN_LIFE, debugger_breakpoint_life_text[ bp->life ],
1067       -1
1068     );
1069 
1070     if( bp->condition ) {
1071       gchar buffer2[80];
1072       debugger_expression_deparse( buffer2, sizeof( buffer2 ), bp->condition );
1073       gtk_list_store_set( breakpoints_model, &it, BREAKPOINTS_COLUMN_CONDITION, buffer2, -1 );
1074     }
1075 
1076   }
1077 }
1078 
1079 static void
update_disassembly(void)1080 update_disassembly( void )
1081 {
1082   size_t i; libspectrum_word address;
1083   GtkTreeIter it;
1084 
1085   gtk_list_store_clear( disassembly_model );
1086 
1087   for( i = 0, address = disassembly_top; i < 20; i++ ) {
1088     size_t l, length;
1089     char buffer1[40], buffer2[40];
1090 
1091     snprintf( buffer1, sizeof( buffer1 ), format_16_bit(), address );
1092     debugger_disassemble( buffer2, sizeof( buffer2 ), &length, address );
1093 
1094     /* pad to 16 characters (long instruction) to avoid varying width */
1095     l = strlen( buffer2 );
1096     while( l < 16 ) buffer2[l++] = ' ';
1097     buffer2[l] = 0;
1098 
1099     gtk_list_store_append( disassembly_model, &it );
1100     gtk_list_store_set( disassembly_model, &it, DISASSEMBLY_COLUMN_ADDRESS, buffer1, DISASSEMBLY_COLUMN_INSTRUCTION, buffer2, -1 );
1101 
1102     address += length;
1103   }
1104 
1105   disassembly_bottom = address;
1106 }
1107 
1108 static void
update_events(void)1109 update_events( void )
1110 {
1111   gtk_list_store_clear( events_model );
1112   event_foreach( add_event, NULL );
1113 }
1114 
1115 static void
add_event(gpointer data,gpointer user_data GCC_UNUSED)1116 add_event( gpointer data, gpointer user_data GCC_UNUSED )
1117 {
1118   event_t *ptr = data;
1119   GtkTreeIter it;
1120 
1121   if( ptr->type != event_type_null ) {
1122     gtk_list_store_append( events_model, &it );
1123     gtk_list_store_set( events_model, &it, EVENTS_COLUMN_TIME, ptr->tstates, EVENTS_COLUMN_TYPE, event_name( ptr->type ), -1 );
1124   }
1125 }
1126 
1127 static int
deactivate_debugger(void)1128 deactivate_debugger( void )
1129 {
1130   gtk_main_quit();
1131   debugger_active = 0;
1132   fuse_emulation_unpause();
1133   return 0;
1134 }
1135 
1136 /* Set the disassembly to start at 'address' */
1137 int
ui_debugger_disassemble(libspectrum_word address)1138 ui_debugger_disassemble( libspectrum_word address )
1139 {
1140   disassembly_top = address;
1141 
1142   /* Block further events while adjusting scrollbar. */
1143   g_signal_handlers_block_by_func( G_OBJECT( disassembly_scrollbar_adjustment ),
1144                                    G_CALLBACK( move_disassembly ), NULL );
1145 
1146   /* Note: GtkAdjustment can not cope with "upper bound - page_size" value and
1147      higher */
1148   gtk_adjustment_set_value( disassembly_scrollbar_adjustment, address );
1149 
1150   /* Enable events for scrollbar */
1151   g_signal_handlers_unblock_by_func( G_OBJECT(disassembly_scrollbar_adjustment),
1152                                      G_CALLBACK( move_disassembly ),  NULL );
1153 
1154   /* And update the disassembly if the debugger is active */
1155   if( debugger_active ) {
1156     update_disassembly();
1157   }
1158 
1159   return 0;
1160 }
1161 
1162 /* Called when the disassembly scrollbar is moved */
1163 static void
move_disassembly(GtkAdjustment * adjustment,gpointer user_data GCC_UNUSED)1164 move_disassembly( GtkAdjustment *adjustment, gpointer user_data GCC_UNUSED )
1165 {
1166   gdouble value;
1167   int cursor_row;
1168   libspectrum_word addresss;
1169 
1170   /* FIXME: Movements are imprecise while dragging the scroll bar */
1171   value = gtk_adjustment_get_value( adjustment );
1172 
1173   cursor_row = gtkui_list_get_cursor( GTK_TREE_VIEW( disassembly ) );
1174 
1175   /* disassembly_top < value <= disassembly_top + 1 => 'down' button pressed
1176      Move the disassembly on by one instruction */
1177   if( value > disassembly_top && value - disassembly_top <= 1 ) {
1178 
1179     addresss = debugger_search_instruction( disassembly_top, 1 );
1180     ui_debugger_disassemble( addresss );
1181 
1182   /* disassembly_top - 1 <= value < disassembly_top => 'up' button pressed
1183 
1184      The desired state after this is for the current top instruction
1185      to be the second instruction shown in the disassembly.
1186 
1187      Unfortunately, it's not trivial to determine where disassembly
1188      should now start, as we have variable length instructions of
1189      unbounded length (multiple DD and FD prefixes on one instruction
1190      are possible).
1191 
1192      In general, we want the _longest_ opcode which produces the
1193      current top in second place (consider something like LD A,nn:
1194      we're not interested if nn happens to represent a one-byte
1195      opcode), so look back a reasonable length (say, 8 bytes) and see
1196      what we find.
1197 
1198      In some cases (eg if we're currently pointing to a data byte of a
1199      multi-byte opcode), it will be impossible to get the current top
1200      second. In this case, just move back a byte.
1201 
1202   */
1203   } else if( value < disassembly_top && disassembly_top - value <= 1 ) {
1204 
1205     addresss = debugger_search_instruction( disassembly_top, -1 );
1206     ui_debugger_disassemble( addresss );
1207 
1208   /* Anything else, just set disassembly_top to that value */
1209   } else if( value != disassembly_top ) {
1210 
1211     ui_debugger_disassemble( value );
1212 
1213   }
1214 
1215   /* Mark selected row */
1216   gtkui_list_set_cursor( GTK_TREE_VIEW( disassembly ), cursor_row );
1217 }
1218 
1219 static gboolean
disassembly_key_press(GtkTreeView * list,GdkEventKey * event,gpointer user_data)1220 disassembly_key_press( GtkTreeView *list, GdkEventKey *event,
1221                        gpointer user_data )
1222 {
1223   GtkAdjustment *adjustment = user_data;
1224   gdouble page_size, page_increment;
1225   int cursor_row;
1226   libspectrum_word initial_top, addresss;
1227 
1228   initial_top = disassembly_top;
1229   page_size = gtk_adjustment_get_page_size( adjustment );
1230   page_increment = gtk_adjustment_get_page_increment( adjustment );
1231 
1232   /* Get selected row */
1233   cursor_row = gtkui_list_get_cursor( list );
1234 
1235   switch( event->keyval ) {
1236 
1237   case GDK_KEY_Down:
1238     if( cursor_row == page_size - 1 ) {
1239       addresss = debugger_search_instruction( disassembly_top, 1 );
1240       ui_debugger_disassemble( addresss );
1241     }
1242     break;
1243 
1244   case GDK_KEY_Up:
1245     if( cursor_row == 0 ) {
1246       addresss = debugger_search_instruction( disassembly_top, -1 );
1247       ui_debugger_disassemble( addresss );
1248     }
1249     break;
1250 
1251   case GDK_KEY_Page_Down:
1252     ui_debugger_disassemble( disassembly_bottom );
1253     break;
1254 
1255   case GDK_KEY_Page_Up:
1256     addresss = debugger_search_instruction( disassembly_top, -page_increment );
1257     ui_debugger_disassemble( addresss );
1258     break;
1259 
1260   case GDK_KEY_Home:
1261     cursor_row = 0;
1262     ui_debugger_disassemble( 0x0000 );
1263     break;
1264 
1265   case GDK_KEY_End:
1266     cursor_row = page_size - 1;
1267     addresss = debugger_search_instruction( 0x0000, -page_size );
1268     ui_debugger_disassemble( addresss );
1269     break;
1270 
1271   default:
1272     return FALSE;
1273   }
1274 
1275   if( initial_top != disassembly_top ) {
1276     update_disassembly();
1277 
1278     /* Mark selected row */
1279     gtkui_list_set_cursor( list, cursor_row );
1280     return TRUE;
1281   }
1282 
1283   return FALSE;
1284 }
1285 
1286 /* Called when the wheel mouse is moved on the list (not on the scrollbar) */
1287 static gboolean
disassembly_wheel_scroll(GtkTreeView * list GCC_UNUSED,GdkEvent * event,gpointer user_data)1288 disassembly_wheel_scroll( GtkTreeView *list GCC_UNUSED, GdkEvent *event,
1289                           gpointer user_data )
1290 {
1291   libspectrum_word initial_top, addresss;
1292   int cursor_row;
1293 
1294   initial_top = disassembly_top;
1295 
1296   /* Get selected row */
1297   cursor_row = gtkui_list_get_cursor( list );
1298 
1299   switch( event->scroll.direction ) {
1300   case GDK_SCROLL_UP:
1301     addresss = debugger_search_instruction( disassembly_top, -1 );
1302     ui_debugger_disassemble( addresss );
1303     break;
1304 
1305   case GDK_SCROLL_DOWN:
1306     addresss = debugger_search_instruction( disassembly_top, 1 );
1307     ui_debugger_disassemble( addresss );
1308     break;
1309 
1310 #if GTK_CHECK_VERSION( 3, 4, 0 )
1311 
1312   case GDK_SCROLL_SMOOTH:
1313     {
1314       GtkAdjustment *adjustment = user_data;
1315       static gdouble total_dy = 0;
1316       gdouble dx, dy, page_size;
1317       int delta;
1318 
1319       if( gdk_event_get_scroll_deltas( event, &dx, &dy ) ) {
1320         /* Calculate number of instructions to jump */
1321         total_dy += dy;
1322         page_size = gtk_adjustment_get_page_size( adjustment );
1323         delta = total_dy * pow( page_size, 2.0 / 3.0 );
1324 
1325         /* Is movement significative? */
1326         if( delta ) {
1327           addresss = debugger_search_instruction( disassembly_top, delta );
1328           ui_debugger_disassemble( addresss );
1329           total_dy = 0;
1330         }
1331       }
1332       break;
1333     }
1334 
1335 #endif
1336 
1337   default:
1338     return FALSE;
1339   }
1340 
1341   if( initial_top != disassembly_top ) {
1342     /* Mark selected row */
1343     gtkui_list_set_cursor( list, cursor_row );
1344     return TRUE;
1345   }
1346 
1347   return FALSE;
1348 }
1349 
1350 /* Evaluate the command currently in the entry box */
1351 static void
evaluate_command(GtkWidget * widget,gpointer user_data GCC_UNUSED)1352 evaluate_command( GtkWidget *widget, gpointer user_data GCC_UNUSED )
1353 {
1354   debugger_command_evaluate( gtk_entry_get_text( GTK_ENTRY( widget ) ) );
1355 }
1356 
1357 static void
gtkui_debugger_done_step(GtkWidget * widget GCC_UNUSED,gpointer user_data GCC_UNUSED)1358 gtkui_debugger_done_step( GtkWidget *widget GCC_UNUSED,
1359 			  gpointer user_data GCC_UNUSED )
1360 {
1361   debugger_step();
1362 }
1363 
1364 static void
gtkui_debugger_done_continue(GtkWidget * widget GCC_UNUSED,gpointer user_data GCC_UNUSED)1365 gtkui_debugger_done_continue( GtkWidget *widget GCC_UNUSED,
1366 			      gpointer user_data GCC_UNUSED )
1367 {
1368   debugger_run();
1369 }
1370 
1371 static void
gtkui_debugger_break(GtkWidget * widget GCC_UNUSED,gpointer user_data GCC_UNUSED)1372 gtkui_debugger_break( GtkWidget *widget GCC_UNUSED,
1373 		      gpointer user_data GCC_UNUSED )
1374 {
1375   debugger_mode = DEBUGGER_MODE_HALTED;
1376   gtk_widget_set_sensitive( continue_button, 1 );
1377   gtk_widget_set_sensitive( break_button, 0 );
1378 }
1379 
1380 static gboolean
delete_dialog(GtkWidget * widget,GdkEvent * event GCC_UNUSED,gpointer user_data)1381 delete_dialog( GtkWidget *widget, GdkEvent *event GCC_UNUSED,
1382 	       gpointer user_data )
1383 {
1384   gtkui_debugger_done_close( widget, user_data );
1385   return TRUE;
1386 }
1387 
1388 static void
gtkui_debugger_done_close(GtkWidget * widget,gpointer user_data GCC_UNUSED)1389 gtkui_debugger_done_close( GtkWidget *widget, gpointer user_data GCC_UNUSED )
1390 {
1391   gtk_widget_hide( widget );
1392   gtkui_debugger_done_continue( NULL, NULL );
1393 }
1394