2 * $Id: debug_win.c,v 1.22 2007-10-08 11:48:56 nkeynes Exp $
3 * This file is responsible for the main debugger gui frame.
5 * Copyright (c) 2005 Nathan Keynes.
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.
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.
22 #include "sh4/sh4dasm.h"
27 #include "pvr2/pvr2.h"
29 GdkColor *msg_colors[] = { &clrError, &clrError, &clrWarn, &clrNormal,
30 &clrDebug, &clrTrace };
31 char *msg_levels[] = { "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" };
32 int global_msg_level = EMIT_WARN;
34 void init_register_list( debug_info_t data );
36 struct debug_info_struct {
40 const struct cpu_desc_struct *cpu;
41 const cpu_desc_t *cpu_list;
44 GtkCList *disasm_list;
47 GtkProgressBar *icounter;
48 char icounter_text[16];
52 debug_info_t init_debug_win(GtkWidget *win, const cpu_desc_t *cpu_list )
56 debug_info_t data = g_malloc0( sizeof(struct debug_info_struct) + cpu_list[0]->regs_size );
57 data->disasm_from = -1;
60 data->cpu = cpu_list[0];
61 data->cpu_list = cpu_list;
63 data->regs_list= gtk_object_get_data(GTK_OBJECT(win), "reg_list");
65 gtk_widget_modify_font( GTK_WIDGET(data->regs_list), fixed_list_font );
66 init_register_list( data );
67 data->msgs_list = gtk_object_get_data(GTK_OBJECT(win), "output_list");
68 data->disasm_list = gtk_object_get_data(GTK_OBJECT(win), "disasm_list");
69 gtk_clist_set_column_width( data->disasm_list, 1, 16 );
70 data->page_field = gtk_object_get_data(GTK_OBJECT(win), "page_field");
72 appbar = gtk_object_get_data(GTK_OBJECT(win), "debug_appbar");
73 data->icounter = gnome_appbar_get_progress( appbar );
74 gtk_progress_bar_set_text(data->icounter, "1");
76 gtk_object_set_data( GTK_OBJECT(win), "debug_data", data );
77 set_disassembly_pc( data, *data->cpu->pc, FALSE );
78 debug_win_set_running( data, FALSE );
82 void init_register_list( debug_info_t data )
88 gtk_clist_clear( data->regs_list );
90 for( i=0; data->cpu->regs_info[i].name != NULL; i++ ) {
91 arr[0] = data->cpu->regs_info[i].name;
92 if( data->cpu->regs_info->type == REG_INT )
93 sprintf( buf, "%08X", *((uint32_t *)data->cpu->regs_info[i].value) );
95 sprintf( buf, "%f", *((float *)data->cpu->regs_info[i].value) );
96 gtk_clist_append( data->regs_list, arr );
101 * Check for changed registers and update the display
103 void update_registers( debug_info_t data )
106 for( i=0; data->cpu->regs_info[i].name != NULL; i++ ) {
107 if( data->cpu->regs_info[i].type == REG_INT ) {
108 /* Yes this _is_ probably fairly evil */
109 if( *((uint32_t *)data->cpu->regs_info[i].value) !=
110 *((uint32_t *)((char *)data->saved_regs + ((char *)data->cpu->regs_info[i].value - (char *)data->cpu->regs))) ) {
112 sprintf( buf, "%08X", *((uint32_t *)data->cpu->regs_info[i].value) );
113 gtk_clist_set_text( data->regs_list, i, 1, buf );
114 gtk_clist_set_foreground( data->regs_list, i, &clrChanged );
116 gtk_clist_set_foreground( data->regs_list, i, &clrNormal );
119 if( *((float *)data->cpu->regs_info[i].value) !=
120 *((float *)((char *)data->saved_regs + ((char *)data->cpu->regs_info[i].value - (char *)data->cpu->regs))) ) {
122 sprintf( buf, "%f", *((float *)data->cpu->regs_info[i].value) );
123 gtk_clist_set_text( data->regs_list, i, 1, buf );
124 gtk_clist_set_foreground( data->regs_list, i, &clrChanged );
126 gtk_clist_set_foreground( data->regs_list, i, &clrNormal );
131 set_disassembly_pc( data, *data->cpu->pc, FALSE );
132 memcpy( data->saved_regs, data->cpu->regs, data->cpu->regs_size );
135 void update_icount( debug_info_t data )
138 // sprintf( data->icounter_text, "%d", *data->cpu->icount );
139 sprintf( data->icounter_text, "%d", pvr2_get_frame_count() );
140 gtk_progress_bar_set_text( data->icounter, data->icounter_text );
144 void set_disassembly_region( debug_info_t data, unsigned int page )
146 uint32_t i, posn, next;
149 char opcode[16] = "";
150 char *arr[4] = { addr, " ", opcode, buf };
151 unsigned int from = page & 0xFFFFF000;
152 unsigned int to = from + 4096;
154 gtk_clist_clear(data->disasm_list);
156 sprintf( addr, "%08X", from );
157 gtk_entry_set_text( data->page_field, addr );
159 if( !data->cpu->is_valid_page_func( from ) ) {
160 arr[3] = "This page is currently unmapped";
161 gtk_clist_append( data->disasm_list, arr );
162 gtk_clist_set_foreground( data->disasm_list, 0, &clrError );
164 for( i=from; i<to; i = next ) {
165 next = data->cpu->disasm_func( i, buf, sizeof(buf), opcode );
166 sprintf( addr, "%08X", i );
167 posn = gtk_clist_append( data->disasm_list, arr );
169 gtk_clist_set_foreground( data->disasm_list, posn, &clrWarn );
170 if( data->cpu->get_breakpoint != NULL ) {
171 int type = data->cpu->get_breakpoint( i );
174 gtk_clist_set_background( data->disasm_list, posn, &clrTempBreak );
177 gtk_clist_set_background( data->disasm_list, posn, &clrBreak );
182 if( data->disasm_pc != -1 && data->disasm_pc >= from && data->disasm_pc < to )
183 gtk_clist_set_foreground( data->disasm_list, address_to_row(data, data->disasm_pc),
187 if( page != from ) { /* not a page boundary */
188 gtk_clist_moveto( data->disasm_list, (page-from)>>1, 0, 0.5, 0.0 );
190 data->disasm_from = from;
191 data->disasm_to = to;
194 void jump_to_disassembly( debug_info_t data, unsigned int addr, gboolean select )
198 if( addr < data->disasm_from || addr >= data->disasm_to )
199 set_disassembly_region(data,addr);
201 row = address_to_row( data, addr );
203 gtk_clist_select_row( data->disasm_list, row, 0 );
205 if( gtk_clist_row_is_visible( data->disasm_list, row ) != GTK_VISIBILITY_FULL ){
206 gtk_clist_moveto( data->disasm_list, row, 0, 0.5, 0.0 );
210 void jump_to_pc( debug_info_t data, gboolean select )
212 jump_to_disassembly( data, *data->cpu->pc, select );
215 void set_disassembly_pc( debug_info_t data, unsigned int pc, gboolean select )
219 jump_to_disassembly( data, pc, select );
220 if( data->disasm_pc != -1 && data->disasm_pc >= data->disasm_from &&
221 data->disasm_pc < data->disasm_to )
222 gtk_clist_set_foreground( data->disasm_list,
223 (data->disasm_pc - data->disasm_from) / data->cpu->instr_size,
225 row = address_to_row( data, pc );
226 gtk_clist_set_foreground( data->disasm_list, row, &clrPC );
227 data->disasm_pc = pc;
230 void set_disassembly_cpu( debug_info_t data, const gchar *cpu )
233 for( i=0; data->cpu_list[i] != NULL; i++ ) {
234 if( strcmp( data->cpu_list[i]->name, cpu ) == 0 ) {
235 if( data->cpu != data->cpu_list[i] ) {
236 data->cpu = data->cpu_list[i];
237 data->disasm_from = data->disasm_to = -1; /* Force reload */
238 set_disassembly_pc( data, *data->cpu->pc, FALSE );
239 init_register_list( data );
240 update_icount( data );
247 void debug_win_toggle_breakpoint( debug_info_t data, int row )
249 uint32_t pc = row_to_address( data, row );
250 int oldType = data->cpu->get_breakpoint( pc );
251 if( oldType != BREAK_NONE ) {
252 data->cpu->clear_breakpoint( pc, oldType );
253 gtk_clist_set_background( data->disasm_list, row, &clrWhite );
255 data->cpu->set_breakpoint( pc, BREAK_KEEP );
256 gtk_clist_set_background( data->disasm_list, row, &clrBreak );
260 void debug_win_set_oneshot_breakpoint( debug_info_t data, int row )
262 uint32_t pc = row_to_address( data, row );
263 data->cpu->clear_breakpoint( pc, BREAK_ONESHOT );
264 data->cpu->set_breakpoint( pc, BREAK_ONESHOT );
265 gtk_clist_set_background( data->disasm_list, row, &clrTempBreak );
269 * Execute a single instruction using the current CPU mode.
271 void debug_win_single_step( debug_info_t data )
273 data->cpu->step_func();
277 uint32_t row_to_address( debug_info_t data, int row ) {
278 return data->cpu->instr_size * row + data->disasm_from;
281 int address_to_row( debug_info_t data, uint32_t address ) {
282 if( data->disasm_from > address || data->disasm_to <= address )
284 return (address - data->disasm_from) / data->cpu->instr_size;
288 void emit( void *ptr, int level, const gchar *source, const char *msg, ... )
290 char buf[20], addr[10] = "", *p;
291 const gchar *arr[4] = {buf, source, addr};
293 time_t tm = time(NULL);
298 else data = (debug_info_t)ptr;
300 if( level > global_msg_level ) {
305 strftime( buf, sizeof(buf), "%H:%M:%S", localtime(&tm) );
308 fprintf( stderr, "%s %08X %-5s ", buf, *sh4_cpu_desc.pc, msg_levels[level] );
309 vfprintf( stderr, msg, ap );
310 fprintf( stderr, "\n" );
315 p = g_strdup_vprintf( msg, ap );
316 sprintf( addr, "%08X", *data->cpu->pc );
318 posn = gtk_clist_append(data->msgs_list, (gchar **)arr);
322 gtk_clist_set_foreground( data->msgs_list, posn, msg_colors[level] );
323 gtk_clist_moveto( data->msgs_list, posn, 0, 1.0, 0.0 );
325 /* emit _really_ slows down the emu, to the point where the gui can be
326 * completely unresponsive if I don't include this:
328 while( gtk_events_pending() )
329 gtk_main_iteration();
332 debug_info_t get_debug_info( GtkWidget *widget ) {
334 GtkWidget *win = gtk_widget_get_toplevel(widget);
335 debug_info_t data = (debug_info_t)gtk_object_get_data( GTK_OBJECT(win), "debug_data" );
339 void debug_win_enable_widget( debug_info_t data, const char *name,
342 GtkWidget *widget = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(data->win), name));
343 gtk_widget_set_sensitive( widget, enabled );
346 void debug_win_set_running( debug_info_t data, gboolean isRunning )
349 debug_win_enable_widget( data, "stop_btn", isRunning );
350 debug_win_enable_widget( data, "step_btn", !isRunning );
351 debug_win_enable_widget( data, "run_btn", !isRunning );
352 debug_win_enable_widget( data, "runto_btn", !isRunning );
.