/* Copyright (C) 1998,1999,2000,2001 T. Scott Dattalo and Ralf Forsberg This file is part of gpsim. gpsim is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. gpsim is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with gpsim; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "../config.h" #ifdef HAVE_GUI #include #include #include #include #include #include #include #include "../src/interface.h" #include "../src/cmd_gpsim.h" #include "gui.h" #include "preferences.h" #include "gui_register.h" #include "gui_regwin.h" #include "gui_watch.h" #define NAMECOL 0 #define DECIMALCOL 2 #define HEXCOL 3 #define ASCIICOL 4 #define BITCOL 5 #define LASTCOL BITCOL static char *watch_titles[]={"name","address","dec","hex","ascii","bits"}; #define COLUMNS sizeof(watch_titles)/sizeof(char*) class ColumnData { public: int column; int isVisible; bool bIsValid; Watch_Window *ww; void SetVisibility(bool bVisibility); void SetValidity(bool ); bool isValid(); void Show(); ColumnData(); } coldata[COLUMNS]; static void select_columns(Watch_Window *ww, GtkWidget *clist); typedef enum { MENU_REMOVE, MENU_SET_VALUE, MENU_BREAK_CLEAR, MENU_BREAK_READ, MENU_BREAK_WRITE, MENU_BREAK_READ_VALUE, MENU_BREAK_WRITE_VALUE, MENU_COLUMNS, } menu_id; typedef struct _menu_item { char *name; menu_id id; GtkWidget *item; } menu_item; static menu_item menu_items[] = { {"Remove watch", MENU_REMOVE}, {"Set value...", MENU_SET_VALUE}, {"Clear breakpoints", MENU_BREAK_CLEAR}, {"Set break on read", MENU_BREAK_READ}, {"Set break on write", MENU_BREAK_WRITE}, {"Set break on read value...", MENU_BREAK_READ_VALUE}, {"Set break on write value...", MENU_BREAK_WRITE_VALUE}, {"Columns...", MENU_COLUMNS}, }; // Used only in popup menus Watch_Window *popup_ww; //======================================================================== class WatchWindowXREF : public CrossReferenceToGUI { public: void Update(int new_value) { Watch_Window *ww = (Watch_Window *) (parent_window); if (ww) ww->UpdateWatch((WatchEntry *)data); } }; //======================================================================== WatchEntry::WatchEntry() : cpu(0) { } //======================================================================== ColumnData::ColumnData() : isVisible(1), bIsValid(true), ww(0) { } void ColumnData::SetVisibility(bool bVisibility) { isVisible = bVisibility ? 1 : 0; Show(); } void ColumnData::Show() { if (ww) { int show = isVisible & (bIsValid ? 1 : 0); gtk_clist_set_column_visibility(GTK_CLIST(ww->watch_clist),column,show); config_set_variable(ww->name(), watch_titles[column],show); } } bool ColumnData::isValid() { return bIsValid; } void ColumnData::SetValidity(bool newValid) { bIsValid = newValid; } //======================================================================== void Watch_Window::ClearWatch(WatchEntry *entry) { gtk_clist_remove(GTK_CLIST(watch_clist),current_row); watches=g_list_remove(watches,entry); entry->Clear_xref(); free(entry); } void Watch_Window::UpdateMenus(void) { GtkWidget *item; WatchEntry *entry; unsigned int i; for (i=0; i < (sizeof(menu_items)/sizeof(menu_items[0])) ; i++) { item=menu_items[i].item; if(menu_items[i].id!=MENU_COLUMNS) { entry = (WatchEntry*) gtk_clist_get_row_data(GTK_CLIST(watch_clist),current_row); if(menu_items[i].id!=MENU_COLUMNS && (entry==0 || (entry->type==REGISTER_EEPROM && menu_items[i].id==MENU_BREAK_CLEAR)|| (entry->type==REGISTER_EEPROM && menu_items[i].id==MENU_BREAK_READ)|| (entry->type==REGISTER_EEPROM && menu_items[i].id==MENU_BREAK_WRITE)|| (entry->type==REGISTER_EEPROM && menu_items[i].id==MENU_BREAK_READ_VALUE)|| (entry->type==REGISTER_EEPROM && menu_items[i].id==MENU_BREAK_WRITE_VALUE) )) gtk_widget_set_sensitive (item, FALSE); else gtk_widget_set_sensitive (item, TRUE); } } } int Watch_Window::set_config(void) { int iRet = GUI_Object::set_config(); WriteSymbolList(); return iRet; } void Watch_Window::WriteSymbolList() { // delete previous list DeleteSymbolList(); // write the current list WatchEntry *entry; guint uSize = g_list_length(watches); char cwv[100]; char *vname; for (guint i = 0; ipRegSymbol) config_set_string(name(), cwv, entry->pRegSymbol->name().c_str()); } } void Watch_Window::DeleteSymbolList() { int i=0; char cwv[100]; while (i<1000) { snprintf(cwv, sizeof(cwv), "WV%d",i++); if (config_remove(name(), cwv) == 0 ) { break; } } } void Watch_Window::ReadSymbolList() { // now read symbols watched from a prior simulation session int i=0; char cwv[100]; char *vname; while (i<1000) { snprintf(cwv, sizeof(cwv), "WV%d",i++); vname = 0; if (config_get_string(name(), cwv, &vname) ) { Value *val = get_symbol_table().find(vname); if(val != 0) { Add(val); } } else break; } } static void unselect_row(GtkCList *clist, gint row, gint column, GdkEvent *event, Watch_Window *ww) { ww->UpdateMenus(); } // called when user has selected a menu item static void popup_activated(GtkWidget *widget, gpointer data) { menu_item *item; WatchEntry *entry; int value; if(widget==0 || data==0) { printf("Warning popup_activated(%p,%p)\n",widget,data); return; } item = (menu_item *)data; entry = (WatchEntry*) gtk_clist_get_row_data(GTK_CLIST(popup_ww->watch_clist),popup_ww->current_row); if(entry==0 && item->id!=MENU_COLUMNS) return; if(!entry || !entry->cpu) return; switch(item->id) { case MENU_REMOVE: popup_ww->ClearWatch(entry); //remove_entry(popup_ww,entry); break; case MENU_SET_VALUE: value = gui_get_value("value:"); if(value<0) break; // Cancel entry->put_value(value); break; case MENU_BREAK_READ: get_bp().set_read_break(entry->cpu,entry->address); break; case MENU_BREAK_WRITE: get_bp().set_write_break(entry->cpu,entry->address); break; case MENU_BREAK_READ_VALUE: value = gui_get_value("value to read for breakpoint:"); if(value<0) break; // Cancel get_bp().set_read_value_break(entry->cpu,entry->address,value); break; case MENU_BREAK_WRITE_VALUE: value = gui_get_value("value to write for breakpoint:"); if(value<0) break; // Cancel get_bp().set_write_value_break(entry->cpu,entry->address,value); break; case MENU_BREAK_CLEAR: get_bp().clear_all_register(entry->cpu,entry->address); break; case MENU_COLUMNS: select_columns(popup_ww, popup_ww->watch_clist); break; default: puts("Unhandled menuitem?"); break; } } //------------------------------------------------------------ // call back function to toggle column visibility in the configuration popup static void set_column(GtkCheckButton *button, ColumnData *coldata) { if (coldata) coldata->SetVisibility(button->toggle_button.active != 0); } static void select_columns(Watch_Window *ww, GtkWidget *clist) { GtkWidget *dialog=0; GtkWidget *button; int i; dialog = gtk_dialog_new(); gtk_container_set_border_width(GTK_CONTAINER(dialog),30); gtk_signal_connect_object(GTK_OBJECT(dialog), "delete_event",GTK_SIGNAL_FUNC(gtk_widget_destroy),GTK_OBJECT(dialog)); for(i=0;ivbox),button,FALSE,FALSE,0); gtk_signal_connect(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(set_column),(gpointer)&coldata[i]); } button = gtk_button_new_with_label("OK"); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE,FALSE,10); gtk_signal_connect_object(GTK_OBJECT(button),"clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),GTK_OBJECT(dialog)); GTK_WIDGET_SET_FLAGS(button,GTK_CAN_DEFAULT); gtk_widget_grab_default(button); gtk_widget_show(dialog); return; } // helper function, called from do_popup static GtkWidget * build_menu(GtkWidget *sheet, Watch_Window *ww) { GtkWidget *menu; GtkWidget *item; unsigned int i; if(sheet==0 || ww==0) { printf("Warning build_menu(%p,%p)\n",sheet,ww); return 0; } popup_ww = ww; menu=gtk_menu_new(); item = gtk_tearoff_menu_item_new (); gtk_menu_append (GTK_MENU (menu), item); gtk_widget_show (item); for (i=0; i < (sizeof(menu_items)/sizeof(menu_items[0])) ; i++){ menu_items[i].item=item=gtk_menu_item_new_with_label(menu_items[i].name); gtk_signal_connect(GTK_OBJECT(item),"activate", (GtkSignalFunc) popup_activated, &menu_items[i]); gtk_widget_show(item); gtk_menu_append(GTK_MENU(menu),item); } ww->UpdateMenus(); return menu; } // button press handler static gint do_popup(GtkWidget *widget, GdkEventButton *event, Watch_Window *ww) { GtkWidget *popup; if(widget==0 || event==0 || ww==0) { printf("Warning do_popup(%p,%p,%p)\n",widget,event,ww); return 0; } popup=ww->popup_menu; if( (event->type == GDK_BUTTON_PRESS) && (event->button == 3) ) gtk_menu_popup(GTK_MENU(popup), 0, 0, 0, 0, 3, event->time); /* WatchEntry *entry; if(event->type==GDK_2BUTTON_PRESS && event->button==1) { int column=ww->current_column; int row=ww->current_row; entry = (WatchEntry*) gtk_clist_get_row_data(GTK_CLIST(ww->watch_clist), row); if(column>=MSBCOL && column<=LSBCOL) { int value; // , bit; printf("column %d\n",column); // Toggle the bit. value = entry->get_value(); value ^= (1<< (15-(column-MSBCOL))); entry->put_value(value); } } */ return 0; } static gint key_press(GtkWidget *widget, GdkEventKey *key, gpointer data) { WatchEntry *entry; Watch_Window *ww = (Watch_Window *) data; if(!ww) return(FALSE); if(!ww->gp) return(FALSE); if(!ww->gp->cpu) return(FALSE); switch(key->keyval) { case GDK_Delete: entry = (WatchEntry*) gtk_clist_get_row_data(GTK_CLIST(ww->watch_clist),ww->current_row); if(entry!=0) ww->ClearWatch(entry); break; } return TRUE; } static gint watch_list_row_selected(GtkCList *watchlist,gint row, gint column,GdkEvent *event, Watch_Window *ww) { WatchEntry *entry; GUI_Processor *gp; ww->current_row=row; ww->current_column=column; gp=ww->gp; entry = (WatchEntry*) gtk_clist_get_row_data(GTK_CLIST(ww->watch_clist), row); if(!entry) return TRUE; if(entry->type==REGISTER_RAM) gp->regwin_ram->SelectRegister(entry->address); else if(entry->type==REGISTER_EEPROM) gp->regwin_eeprom->SelectRegister(entry->address); ww->UpdateMenus(); return 0; } static void watch_click_column(GtkCList *clist, int column) { static int last_col=-1; static GtkSortType last_sort_type=GTK_SORT_DESCENDING; if(last_col==-1) last_col=column; if(last_col == column) { if(last_sort_type==GTK_SORT_DESCENDING) { gtk_clist_set_sort_type(clist,GTK_SORT_ASCENDING); last_sort_type=GTK_SORT_ASCENDING; } else { gtk_clist_set_sort_type(clist,GTK_SORT_DESCENDING); last_sort_type=GTK_SORT_DESCENDING; } } gtk_clist_set_sort_column(clist,column); gtk_clist_sort(clist); last_col=column; } static int delete_event(GtkWidget *widget, GdkEvent *event, Watch_Window *ww) { ww->ChangeView(VIEW_HIDE); return TRUE; } //======================================================================== // UpdateWatch // A single watch entry is updated here. Here's what's done: // // If the value has not changed since the last update, then the // foreground and background colors are refreshed and we return. // // If the value has changed, then each of the value fields are refreshed. // Then the foreground and background are refreshed. void Watch_Window::UpdateWatch(WatchEntry *entry) { int row; row=gtk_clist_find_row_from_data(GTK_CLIST(watch_clist),entry); if(row==-1) return; RegisterValue rvNewValue = entry->getRV(); // If the value has not changed, then simply update the foreground and background // colors and return. if (entry->get_shadow() == rvNewValue) { gtk_clist_set_foreground(GTK_CLIST(watch_clist), row, gColors.normal_fg()); gtk_clist_set_background(GTK_CLIST(watch_clist), row, (entry->hasBreak() ? gColors.breakpoint() : gColors.normal_bg())); return; } RegisterValue rvMaskedNewValue; unsigned int uBitmask; entry->put_shadow(rvNewValue); if(entry->pRegSymbol) { rvMaskedNewValue = *(entry->pRegSymbol); uBitmask = entry->pRegSymbol->getBitmask(); } else { rvMaskedNewValue = entry->getRV(); uBitmask = entry->cpu->register_mask(); } char str[80]; if(rvNewValue.init & uBitmask) { strcpy(str, "?"); } else sprintf(str,"%d", rvNewValue.data); gtk_clist_set_text(GTK_CLIST(watch_clist), row, DECIMALCOL, str); // Hexadecimal representation: rvMaskedNewValue.toString(str, 80); gtk_clist_set_text(GTK_CLIST(watch_clist), row, HEXCOL, str); // ASCII representation str[0] = (rvNewValue.data>'0' && rvNewValue.data<='z') ? rvNewValue.data : 0; str[1] =0; gtk_clist_set_text(GTK_CLIST(watch_clist), row, ASCIICOL, str); // Bit representation char sBits[25]; rvNewValue.toBitStr(sBits, 25, entry->cpu->register_mask(), NULL); gtk_clist_set_text(GTK_CLIST(watch_clist), row, BITCOL, sBits); // Set foreground and background colors gtk_clist_set_foreground(GTK_CLIST(watch_clist), row, gColors.item_has_changed()); gtk_clist_set_background(GTK_CLIST(watch_clist), row, (entry->hasBreak() ? gColors.breakpoint() : gColors.normal_bg())); } //------------------------------------------------------------------------ // Update // // void Watch_Window::Update() { GList *iter; WatchEntry *entry; int clist_frozen=0; iter=watches; while(iter) { entry=(WatchEntry*)iter->data; if (entry) UpdateWatch(entry); iter=iter->next; } if(clist_frozen) gtk_clist_thaw(GTK_CLIST(watch_clist)); } //------------------------------------------------------------------------ void Watch_Window::Add( REGISTER_TYPE type, GUIRegister *reg) { if(!gp || !gp->cpu || !reg || !reg->bIsValid()) return; Register *cpu_reg = reg->get_register(); register_symbol * pRegSym = get_symbol_table().findRegisterSymbol( cpu_reg->address); Add(type, reg, pRegSym); } void Watch_Window::Add( REGISTER_TYPE type, GUIRegister *reg, register_symbol * pRegSym) { char vname[50], addressstring[50]; char *entry[COLUMNS]={vname, addressstring, "", "","",""}; int row; WatchWindowXREF *cross_reference; WatchEntry *watch_entry; if(!gp || !gp->cpu || !reg || !reg->bIsValid()) return; if(!enabled) Build(); Register *cpu_reg; if(pRegSym == 0) { cpu_reg = reg->get_register(); strncpy(vname,cpu_reg->name().c_str(),sizeof(vname)); } else { cpu_reg = pRegSym->getReg(); strncpy(vname,pRegSym->name().c_str(),sizeof(vname)); } unsigned int uAddrMask = 0; unsigned int uLastAddr = gp->cpu->register_memory_size() - 1; while(uLastAddr) { uLastAddr>>=4; uAddrMask<<=4; uAddrMask |= 0xf; } strcpy(addressstring, GetUserInterface().FormatProgramAddress( cpu_reg->address, uAddrMask, IUserInterface::eHex)); gtk_clist_freeze(GTK_CLIST(watch_clist)); row=gtk_clist_append(GTK_CLIST(watch_clist), entry); watch_entry = new WatchEntry(); watch_entry->address=reg->address; watch_entry->pRegSymbol = pRegSym; watch_entry->cpu = gp->cpu; watch_entry->type=type; watch_entry->rma = reg->rma; gtk_clist_set_row_data(GTK_CLIST(watch_clist), row, (gpointer)watch_entry); watches = g_list_append(watches, (gpointer)watch_entry); UpdateWatch(watch_entry); cross_reference = new WatchWindowXREF(); cross_reference->parent_window_type = WT_watch_window; cross_reference->parent_window = (gpointer) this; cross_reference->data = (gpointer) watch_entry; watch_entry->Assign_xref(cross_reference); gtk_clist_thaw(GTK_CLIST(watch_clist)); UpdateMenus(); } //--- // Add - given a symbol, verify that it is a RAM or EEPROM register // symbol. If it is then extract the register and use it's address // to get the GUI representation of the register. void Watch_Window::Add( Value *regSym) { if(regSym && gp) { register_symbol *rs = dynamic_cast(regSym); if(rs != 0) { Register *reg = rs->getReg(); if(reg) { GUIRegister *greg = gp->m_pGUIRamRegisters->Get(reg->address); Add(REGISTER_RAM, greg, rs); } } } } //------------------------------------------------------------------------ // NewSymbols // void Watch_Window::NewProcessor(GUI_Processor *_gp) { if (!gp || !gp->cpu) return; ReadSymbolList(); } //------------------------------------------------------------------------ // ClearWatches // // void Watch_Window::ClearWatches(void) { GList *iter; WatchEntry *entry; int row; iter=watches; while(iter) { entry=(WatchEntry*)iter->data; row=gtk_clist_find_row_from_data(GTK_CLIST(watch_clist),entry); gtk_clist_remove(GTK_CLIST(watch_clist),row); entry->Clear_xref(); free(entry); iter=iter->next; } while( (watches=g_list_remove_link(watches,watches))!=0) ; } //------------------------------------------------------------------------ // Build // // void Watch_Window::Build(void) { if(bIsBuilt) return; GtkWidget *vbox; GtkWidget *scrolled_window; int i; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), "Watch Viewer"); gtk_window_set_default_size(GTK_WINDOW(window), width,height); gtk_widget_set_uposition(GTK_WIDGET(window),x,y); gtk_window_set_wmclass(GTK_WINDOW(window),name(),"Gpsim"); gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC(delete_event), (gpointer)this); gtk_signal_connect_after(GTK_OBJECT(window), "configure_event", GTK_SIGNAL_FUNC(gui_object_configure_event),this); watch_clist = gtk_clist_new_with_titles(COLUMNS,watch_titles); gtk_widget_show(watch_clist); for(i=0;i