Search
lxdream.org :: lxdream/src/gui/debug_win.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/gui/debug_win.c
changeset 457:af605fd32c0b
prev455:3080881d00d4
next480:d28c2992f5ee
author nkeynes
date Wed Oct 31 09:10:23 2007 +0000 (16 years ago)
permissions -rw-r--r--
last change Add save/restore of render buffers in save states
Gzip memory blocks in save states
Move front-buffer management back to pvr2
Add screenshot preview when loading save states
Various minor fixes and cleanups
view annotate diff log raw
     1 /**
     2  * $Id: debug_win.c,v 1.26 2007-10-21 11:38:02 nkeynes Exp $
     3  * This file is responsible for the main debugger gui frame.
     4  *
     5  * Copyright (c) 2005 Nathan Keynes.
     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 #include <stdlib.h>
    18 #include <stdarg.h>
    19 #include <stdio.h>
    20 #include <math.h>
    21 #include <gnome.h>
    22 #include "mem.h"
    23 #include "cpu.h"
    24 #include "gui/gtkui.h"
    25 #include "sh4/sh4dasm.h"
    26 #include "aica/armdasm.h"
    28 GdkColor *msg_colors[] = { &gui_colour_error, &gui_colour_error, &gui_colour_warn, 
    29 			   &gui_colour_normal,&gui_colour_debug, &gui_colour_trace };
    31 const cpu_desc_t cpu_list[4] = { &sh4_cpu_desc, &arm_cpu_desc, &armt_cpu_desc, NULL };
    33 void init_register_list( debug_window_t data );
    34 uint32_t row_to_address( debug_window_t data, int row );
    35 void set_disassembly_pc( debug_window_t data, unsigned int pc, gboolean select );
    36 void set_disassembly_region( debug_window_t data, unsigned int page );
    37 void set_disassembly_cpu( debug_window_t data, const gchar *cpu );
    39 void on_mode_field_changed ( GtkEditable *editable, gpointer user_data);
    40 gboolean on_page_field_key_press_event( GtkWidget * widget, GdkEventKey *event,
    41                                         gpointer user_data);
    42 void on_jump_pc_btn_clicked( GtkButton *button, gpointer user_data);
    43 void on_disasm_list_select_row (GtkCList *clist, gint row, gint column,
    44 				GdkEvent *event, gpointer user_data);
    45 void on_disasm_list_unselect_row (GtkCList *clist, gint row, gint column,
    46 				  GdkEvent *event, gpointer user_data);
    47 gboolean on_debug_delete_event(GtkWidget *widget, GdkEvent event, gpointer user_data);
    49 struct debug_window_info {
    50     int disasm_from;
    51     int disasm_to;
    52     int disasm_pc;
    53     const struct cpu_desc_struct *cpu;
    54     const cpu_desc_t *cpu_list;
    55     GtkCList *regs_list;
    56     GtkCList *disasm_list;
    57     GtkEntry *page_field;
    58     GtkWidget *window;
    59     GtkWidget *statusbar;
    60     char saved_regs[0];
    61 };
    63 debug_window_t debug_window_new( const gchar *title, GtkWidget *menubar, 
    64 				 GtkWidget *toolbar, GtkAccelGroup *accel_group )
    65 {
    66     debug_window_t data = g_malloc0( sizeof(struct debug_window_info) + cpu_list[0]->regs_size );
    67         GtkWidget *vbox;
    68     GtkWidget *frame;
    70     data->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    71     gtk_window_set_default_size (GTK_WINDOW (data->window), 1000, 700);
    72     gtk_window_set_title( GTK_WINDOW(data->window), title );
    73     gtk_window_add_accel_group (GTK_WINDOW (data->window), accel_group);
    75     gtk_toolbar_set_style( GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS );
    77     data->statusbar = gtk_statusbar_new();
    79     GtkWidget *hpaned = gtk_hpaned_new ();
    80     gtk_paned_set_position (GTK_PANED (hpaned), 800);
    82     GtkWidget *disasm_box = gtk_vbox_new(FALSE,0);
    83     gtk_paned_pack1 (GTK_PANED (hpaned), disasm_box, TRUE, TRUE);
    85     GtkWidget *hbox1 = gtk_hbox_new (FALSE, 0);
    86     gtk_box_pack_start (GTK_BOX (disasm_box), hbox1, FALSE, FALSE, 3);
    87     gtk_box_pack_start (GTK_BOX (hbox1), gtk_label_new (_("Page")), FALSE, FALSE, 4);
    89     data->page_field = GTK_ENTRY(gtk_entry_new ());
    90     gtk_box_pack_start (GTK_BOX (hbox1), GTK_WIDGET(data->page_field), FALSE, TRUE, 0);
    92     GtkWidget *jump_pc_btn = gtk_button_new_with_mnemonic (_(" Jump to PC "));
    93     gtk_box_pack_start (GTK_BOX (hbox1), jump_pc_btn, FALSE, FALSE, 4);
    95     gtk_box_pack_start (GTK_BOX (hbox1), gtk_label_new(_("Mode")), FALSE, FALSE, 5);
    97     GtkWidget *mode_box = gtk_combo_new ();
    98     gtk_box_pack_start (GTK_BOX (hbox1), mode_box, FALSE, FALSE, 0);
    99     GList *mode_box_items = NULL;
   100     mode_box_items = g_list_append (mode_box_items, (gpointer) _("SH4"));
   101     mode_box_items = g_list_append (mode_box_items, (gpointer) _("ARM7"));
   102     mode_box_items = g_list_append (mode_box_items, (gpointer) _("ARM7T"));
   103     gtk_combo_set_popdown_strings (GTK_COMBO (mode_box), mode_box_items);
   104     g_list_free (mode_box_items);
   106     GtkWidget *mode_field = GTK_COMBO (mode_box)->entry;
   107     gtk_editable_set_editable (GTK_EDITABLE (mode_field), FALSE);
   109     GtkWidget *disasm_scroll = gtk_scrolled_window_new (NULL, NULL);
   110     gtk_box_pack_start (GTK_BOX (disasm_box), disasm_scroll, TRUE, TRUE, 0);
   111     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (disasm_scroll), GTK_SHADOW_IN);
   112     data->disasm_list = GTK_CLIST(gtk_clist_new (4));
   113     gtk_clist_set_column_width (GTK_CLIST (data->disasm_list), 0, 80);
   114     gtk_clist_set_column_width (GTK_CLIST (data->disasm_list), 2, 80);
   115     gtk_clist_set_column_width (GTK_CLIST (data->disasm_list), 3, 80);
   116     gtk_clist_set_column_width( data->disasm_list, 1, 16 );
   117     gtk_clist_column_titles_hide (GTK_CLIST (data->disasm_list));
   118     gtk_container_add (GTK_CONTAINER (disasm_scroll), GTK_WIDGET(data->disasm_list));
   120     GtkWidget *reg_scroll = gtk_scrolled_window_new (NULL, NULL);
   121     gtk_paned_pack2 (GTK_PANED (hpaned), reg_scroll, FALSE, TRUE);
   122     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (reg_scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
   123     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (reg_scroll), GTK_SHADOW_IN);
   125     data->regs_list = GTK_CLIST(gtk_clist_new (2));
   126     gtk_container_add (GTK_CONTAINER (reg_scroll), GTK_WIDGET(data->regs_list));
   127     gtk_clist_set_column_width (GTK_CLIST (data->regs_list), 0, 80);
   128     gtk_clist_set_column_width (GTK_CLIST (data->regs_list), 1, 80);
   129     gtk_clist_column_titles_hide (GTK_CLIST (data->regs_list));
   130     gtk_widget_modify_font( GTK_WIDGET(data->regs_list), gui_fixed_font );
   132     vbox = gtk_vbox_new(FALSE, 0);
   133     gtk_container_add( GTK_CONTAINER(data->window), vbox );
   134     gtk_box_pack_start( GTK_BOX(vbox), menubar, FALSE, FALSE, 0 );
   135     gtk_box_pack_start( GTK_BOX(vbox), toolbar, FALSE, FALSE, 0 );
   136     gtk_box_pack_start( GTK_BOX(vbox), hpaned, TRUE, TRUE, 0 );
   137     gtk_box_pack_start( GTK_BOX(vbox), data->statusbar, FALSE, FALSE, 0 );
   139     g_signal_connect ((gpointer) data->page_field, "key_press_event",
   140 		      G_CALLBACK (on_page_field_key_press_event),
   141 		      data);
   142     g_signal_connect ((gpointer) jump_pc_btn, "clicked",
   143 		      G_CALLBACK (on_jump_pc_btn_clicked),
   144 		      data);
   145     g_signal_connect ((gpointer) mode_field, "changed",
   146 		      G_CALLBACK (on_mode_field_changed),
   147 		      data);
   148     g_signal_connect ((gpointer) data->disasm_list, "select_row",
   149 		      G_CALLBACK (on_disasm_list_select_row),
   150 		      data);
   151     g_signal_connect ((gpointer) data->disasm_list, "unselect_row",
   152 		      G_CALLBACK (on_disasm_list_unselect_row),
   153 		      data);
   154     g_signal_connect ((gpointer) data->window, "delete_event",
   155 		      G_CALLBACK (on_debug_delete_event),
   156 		      data);
   158     data->disasm_from = -1;
   159     data->disasm_to = -1;
   160     data->disasm_pc = -1;
   161     data->cpu = cpu_list[0];
   162     data->cpu_list = cpu_list;
   164     init_register_list( data );
   165     gtk_object_set_data( GTK_OBJECT(data->window), "debug_data", data );
   166     set_disassembly_pc( data, *data->cpu->pc, FALSE );
   167     debug_window_set_running( data, FALSE );
   169     gtk_widget_show_all( data->window );
   170     return data;
   171 }
   173 void debug_window_show( debug_window_t data, gboolean show )
   174 {
   175     if( show ) {
   176 	gtk_widget_show( data->window );
   177     } else {
   178 	gtk_widget_hide( data->window );
   179     }
   180 }
   182 int debug_window_get_selected_row( debug_window_t data )
   183 {
   184     if( data->disasm_list->selection == NULL ) {
   185 	return -1;
   186     } else {
   187 	return GPOINTER_TO_INT(data->disasm_list->selection->data);
   188     }
   189 }
   191 void init_register_list( debug_window_t data ) 
   192 {
   193     int i;
   194     char buf[20];
   195     char *arr[2];
   197     gtk_clist_clear( data->regs_list );
   198     arr[1] = buf;
   199     for( i=0; data->cpu->regs_info[i].name != NULL; i++ ) {
   200         arr[0] = data->cpu->regs_info[i].name;
   201         if( data->cpu->regs_info->type == REG_INT )
   202             sprintf( buf, "%08X", *((uint32_t *)data->cpu->regs_info[i].value) );
   203         else
   204             sprintf( buf, "%f", *((float *)data->cpu->regs_info[i].value) );
   205         gtk_clist_append( data->regs_list, arr );
   206     }
   207 }
   209 /*
   210  * Check for changed registers and update the display
   211  */
   212 void debug_window_update( debug_window_t data )
   213 {
   214     int i;
   215     for( i=0; data->cpu->regs_info[i].name != NULL; i++ ) {
   216         if( data->cpu->regs_info[i].type == REG_INT ) {
   217             /* Yes this _is_ probably fairly evil */
   218             if( *((uint32_t *)data->cpu->regs_info[i].value) !=
   219                 *((uint32_t *)((char *)data->saved_regs + ((char *)data->cpu->regs_info[i].value - (char *)data->cpu->regs))) ) {
   220                 char buf[20];
   221                 sprintf( buf, "%08X", *((uint32_t *)data->cpu->regs_info[i].value) );
   222                 gtk_clist_set_text( data->regs_list, i, 1, buf );
   223                 gtk_clist_set_foreground( data->regs_list, i, &gui_colour_changed );
   224             } else {
   225                 gtk_clist_set_foreground( data->regs_list, i, &gui_colour_normal );
   226             }
   227         } else {
   228             if( *((float *)data->cpu->regs_info[i].value) !=
   229                 *((float *)((char *)data->saved_regs + ((char *)data->cpu->regs_info[i].value - (char *)data->cpu->regs))) ) {
   230                 char buf[20];
   231                 sprintf( buf, "%f", *((float *)data->cpu->regs_info[i].value) );
   232                 gtk_clist_set_text( data->regs_list, i, 1, buf );
   233                 gtk_clist_set_foreground( data->regs_list, i, &gui_colour_changed );
   234             } else {
   235                 gtk_clist_set_foreground( data->regs_list, i, &gui_colour_normal );
   236             }
   237         }
   238     }
   240     set_disassembly_pc( data, *data->cpu->pc, TRUE );
   241     memcpy( data->saved_regs, data->cpu->regs, data->cpu->regs_size );
   242 }
   244 void set_disassembly_region( debug_window_t data, unsigned int page )
   245 {
   246     uint32_t i, posn, next;
   247     char buf[80];
   248     char addr[10];
   249     char opcode[16] = "";
   250     char *arr[4] = { addr, " ", opcode, buf };
   251     unsigned int from = page & 0xFFFFF000;
   252     unsigned int to = from + 4096;
   254     gtk_clist_clear(data->disasm_list);
   256     sprintf( addr, "%08X", from );
   257     gtk_entry_set_text( data->page_field, addr );
   259     if( !data->cpu->is_valid_page_func( from ) ) {
   260         arr[3] = "This page is currently unmapped";
   261         gtk_clist_append( data->disasm_list, arr );
   262         gtk_clist_set_foreground( data->disasm_list, 0, &gui_colour_error );
   263     } else {
   264         for( i=from; i<to; i = next ) {
   265 	    next = data->cpu->disasm_func( i, buf, sizeof(buf), opcode );
   266             sprintf( addr, "%08X", i );
   267             posn = gtk_clist_append( data->disasm_list, arr );
   268             if( buf[0] == '?' )
   269                 gtk_clist_set_foreground( data->disasm_list, posn, &gui_colour_warn );
   270 	    if( data->cpu->get_breakpoint != NULL ) {
   271 		int type = data->cpu->get_breakpoint( i );
   272 		switch(type) {
   273 		case BREAK_ONESHOT:
   274 		    gtk_clist_set_background( data->disasm_list, posn, &gui_colour_temp_break );
   275 		    break;
   276 		case BREAK_KEEP:
   277 		    gtk_clist_set_background( data->disasm_list, posn, &gui_colour_break );
   278 		    break;
   279 		}
   280 	    }
   281         }
   282         if( data->disasm_pc != -1 && data->disasm_pc >= from && data->disasm_pc < to )
   283             gtk_clist_set_foreground( data->disasm_list, address_to_row(data, data->disasm_pc),
   284                                       &gui_colour_pc );
   285     }
   287     if( page != from ) { /* not a page boundary */
   288         gtk_clist_moveto( data->disasm_list, (page-from)>>1, 0, 0.5, 0.0 );
   289     }
   290     data->disasm_from = from;
   291     data->disasm_to = to;
   292 }
   294 void jump_to_disassembly( debug_window_t data, unsigned int addr, gboolean select )
   295 {
   296     int row;
   298     if( addr < data->disasm_from || addr >= data->disasm_to )
   299         set_disassembly_region(data,addr);
   301     row = address_to_row( data, addr );
   302     if(select) {
   303         gtk_clist_select_row( data->disasm_list, row, 0 );
   304     }
   305     if( gtk_clist_row_is_visible( data->disasm_list, row ) != GTK_VISIBILITY_FULL ){
   306         gtk_clist_moveto( data->disasm_list, row, 0, 0.5, 0.0 );
   307     }
   308 }
   310 void jump_to_pc( debug_window_t data, gboolean select )
   311 {
   312     jump_to_disassembly( data, *data->cpu->pc, select );
   313 }
   315 void set_disassembly_pc( debug_window_t data, unsigned int pc, gboolean select )
   316 {
   317     int row;
   319     jump_to_disassembly( data, pc, select );
   320     if( data->disasm_pc != -1 && data->disasm_pc >= data->disasm_from && 
   321 	data->disasm_pc < data->disasm_to )
   322         gtk_clist_set_foreground( data->disasm_list, 
   323 				  (data->disasm_pc - data->disasm_from) / data->cpu->instr_size,
   324                                   &gui_colour_normal );
   325     row = address_to_row( data, pc );
   326     gtk_clist_set_foreground( data->disasm_list, row, &gui_colour_pc );
   327     data->disasm_pc = pc;
   328 }
   330 void set_disassembly_cpu( debug_window_t data, const gchar *cpu )
   331 {
   332     int i;
   333     for( i=0; data->cpu_list[i] != NULL; i++ ) {
   334 	if( strcmp( data->cpu_list[i]->name, cpu ) == 0 ) {
   335 	    if( data->cpu != data->cpu_list[i] ) {
   336 		data->cpu = data->cpu_list[i];
   337 		data->disasm_from = data->disasm_to = -1; /* Force reload */
   338 		set_disassembly_pc( data, *data->cpu->pc, FALSE );
   339 		init_register_list( data );
   340 	    }
   341 	    return;
   342 	}
   343     }
   344 }
   346 void debug_window_toggle_breakpoint( debug_window_t data, int row )
   347 {
   348     uint32_t pc = row_to_address( data, row );
   349     int oldType = data->cpu->get_breakpoint( pc );
   350     if( oldType != BREAK_NONE ) {
   351 	data->cpu->clear_breakpoint( pc, oldType );
   352 	gtk_clist_set_background( data->disasm_list, row, &gui_colour_white );
   353     } else {
   354 	data->cpu->set_breakpoint( pc, BREAK_KEEP );
   355 	gtk_clist_set_background( data->disasm_list, row, &gui_colour_break );
   356     }
   357 }
   359 void debug_window_set_oneshot_breakpoint( debug_window_t data, int row )
   360 {
   361     uint32_t pc = row_to_address( data, row );
   362     data->cpu->clear_breakpoint( pc, BREAK_ONESHOT );
   363     data->cpu->set_breakpoint( pc, BREAK_ONESHOT );
   364     gtk_clist_set_background( data->disasm_list, row, &gui_colour_temp_break );
   365 }
   367 /**
   368  * Execute a single instruction using the current CPU mode.
   369  */
   370 void debug_window_single_step( debug_window_t data )
   371 {
   372     data->cpu->step_func();
   373     gtk_gui_update();
   374 }
   376 uint32_t row_to_address( debug_window_t data, int row ) {
   377     return data->cpu->instr_size * row + data->disasm_from;
   378 }
   380 int address_to_row( debug_window_t data, uint32_t address ) {
   381     if( data->disasm_from > address || data->disasm_to <= address )
   382 	return -1;
   383     return (address - data->disasm_from) / data->cpu->instr_size;
   384 }
   386 debug_window_t get_debug_info( GtkWidget *widget ) {
   388     GtkWidget *win = gtk_widget_get_toplevel(widget);
   389     debug_window_t data = (debug_window_t)gtk_object_get_data( GTK_OBJECT(win), "debug_data" );
   390     return data;
   391 }
   393 void debug_window_set_running( debug_window_t data, gboolean isRunning ) 
   394 {
   395     if( data != NULL ) {
   396 	gtk_gui_enable_action( "SingleStep", !isRunning );
   397 	gtk_gui_enable_action( "RunTo", !isRunning );
   398     }
   399 }
   401 void on_mode_field_changed ( GtkEditable *editable, gpointer user_data)
   402 {
   403     const gchar *text = gtk_entry_get_text( GTK_ENTRY(editable) );
   404     set_disassembly_cpu( gtk_gui_get_debugger(), text );
   405 }
   408 gboolean on_page_field_key_press_event( GtkWidget * widget, GdkEventKey *event,
   409                                         gpointer user_data)
   410 {
   411     if( event->keyval == GDK_Return || event->keyval == GDK_Linefeed ) {
   412 	debug_window_t data = get_debug_info(widget);
   413         const gchar *text = gtk_entry_get_text( GTK_ENTRY(widget) );
   414         gchar *endptr;
   415         unsigned int val = strtoul( text, &endptr, 16 );
   416         if( text == endptr ) { /* invalid input */
   417             char buf[10];
   418             sprintf( buf, "%08X", row_to_address(data,0) );
   419             gtk_entry_set_text( GTK_ENTRY(widget), buf );
   420         } else {
   421             set_disassembly_region(data, val);
   422         }
   423     }
   424     return FALSE;
   425 }
   428 void on_jump_pc_btn_clicked( GtkButton *button, gpointer user_data)
   429 {
   430     debug_window_t data = get_debug_info( GTK_WIDGET(button) );
   431     jump_to_pc( data, TRUE );
   432 }
   434 void on_disasm_list_select_row (GtkCList *clist, gint row, gint column,
   435 				GdkEvent *event, gpointer user_data)
   436 {
   437     gtk_gui_enable_action( "SetBreakpoint", TRUE );
   438     gtk_gui_enable_action( "RunTo", TRUE );
   439 }
   441 void on_disasm_list_unselect_row (GtkCList *clist, gint row, gint column,
   442 				  GdkEvent *event, gpointer user_data)
   443 {
   444     gtk_gui_enable_action( "SetBreakpoint", FALSE );
   445     gtk_gui_enable_action( "RunTo", FALSE );
   446 }
   448 gboolean on_debug_delete_event(GtkWidget *widget, GdkEvent event, gpointer user_data)
   449 {
   450     gtk_widget_hide( widget );
   451     return TRUE;
   452 }
.