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