Canola  0.8.D001
canola/debugger/gnome.cc
Go to the documentation of this file.
00001 //
00002 // canola - canon canola 1614p emulator
00003 // Copyright (C) 2011, 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License, version 3, as
00007 // published by the Free Software Foundation.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program. If not, see <http://www.gnu.org/licenses/>.
00016 //
00017 
00018 #include <lib/config.h>
00019 #include <libexplain/fopen.h>
00020 
00021 #include <lib/std_string_printf.h>
00022 
00023 #include <canola/debugger/gnome.h>
00024 
00025 
00026 debugger_gnome::~debugger_gnome()
00027 {
00028 }
00029 
00030 
00031 static void
00032 mono(Gtk::TextView &w)
00033 {
00034     w.set_editable(false);
00035 
00036     Pango::FontDescription fdesc;
00037     fdesc.set_family("monospace");
00038     w.override_font(fdesc);
00039 
00040     // create a "red" tag so that we can use it later
00041     Glib::RefPtr<Gtk::TextTag> tp =
00042         w.get_buffer()->create_tag("red");
00043     tp->property_foreground().set_value("red");
00044 }
00045 
00046 
00047 debugger_gnome::debugger_gnome() :
00048     x_reg_name("X Register:"),
00049     y_reg_name("Y Register:"),
00050     address_name("Address:"),
00051     address_value(Gtk::Adjustment::create(1, 1, 240, 1)),
00052     address_value_changed_signal_enable(false),
00053     address_value_changed_signal_instance(),
00054     opcode_name("Opcode:"),
00055     mstate_name("State:")
00056 {
00057     const int spacing = 20;
00058 
00059     left_panel.set_row_spacings(1);
00060 
00061     for (int j = 0; j < 14; ++j)
00062     {
00063         std::string text = std_string_printf("Memory %d:", j + 1);
00064         memory_name[j].set_text(text);
00065     }
00066 
00067     left_panel.attach(address_name,  0, 1, 0, 1);
00068     left_panel.attach(address_value, 1, 2, 0, 1);
00069     address_name.set_alignment(Gtk::ALIGN_START);
00070     address_value.set_wrap();
00071     address_value.set_numeric();
00072     address_value.signal_value_changed().connect
00073     (
00074         sigc::mem_fun(*this, &debugger_gnome::on_address_value_changed)
00075     );
00076 
00077     left_panel.attach(opcode_name,   0, 1, 1, 2);
00078     left_panel.attach(opcode_value,  1, 2, 1, 2);
00079     opcode_name.set_alignment(Gtk::ALIGN_START);
00080     mono(opcode_value);
00081     opcode_value.set_size_request(100, -1);
00082 
00083     left_panel.attach(x_reg_name,    0, 1, 2, 3);
00084     left_panel.attach(x_reg_value,   1, 2, 2, 3);
00085     mono(x_reg_value);
00086     x_reg_name.set_alignment(Gtk::ALIGN_START);
00087 
00088     left_panel.attach(y_reg_name,    0, 1, 3, 4);
00089     left_panel.attach(y_reg_value,   1, 2, 3, 4);
00090     mono(y_reg_value);
00091     y_reg_name.set_alignment(Gtk::ALIGN_START);
00092 
00093     left_panel.attach(mstate_name,   0, 1, 4, 5);
00094     left_panel.attach(mstate_value,  1, 2, 4, 5);
00095     mono(mstate_value);
00096     mstate_name.set_alignment(Gtk::ALIGN_START);
00097 
00098     for (int j = 0; j < 14; ++j)
00099     {
00100         left_panel.attach(memory_name[j],  0, 1, 5 + j, 6 + j);
00101         left_panel.attach(memory_value[j], 1, 2, 5 + j, 6 + j);
00102         mono(memory_value[j]);
00103         memory_name[j].set_alignment(Gtk::ALIGN_START);
00104     }
00105     mono(source_code);
00106     source_code_scroller.add(source_code);
00107     source_code_scroller.set_policy(Gtk::POLICY_AUTOMATIC,
00108         Gtk::POLICY_AUTOMATIC);
00109     source_code_frame.add(source_code_scroller);
00110 
00111     pack_start(left_panel, Gtk::PACK_SHRINK);
00112     pack_start(source_code_frame);
00113     set_spacing(spacing);
00114 
00115     set_border_width(spacing);
00116     show_all_children();
00117 
00118     address_value_changed_signal_enable = true;
00119 }
00120 
00121 
00122 static void
00123 red_number(Gtk::TextView &tv, const std::string &text)
00124 {
00125     tv.get_buffer()->set_text("");
00126     tv.get_buffer()->insert_with_tag(tv.get_buffer()->end(), text, "red");
00127 }
00128 
00129 
00130 static void
00131 black_number(Gtk::TextView &tv, const std::string &text)
00132 {
00133     tv.get_buffer()->set_text(text);
00134 }
00135 
00136 
00137 static void
00138 textview_number(Gtk::TextView &tv, const number &value)
00139 {
00140     if (value.sign() < 0)
00141         red_number(tv, value.to_string());
00142     else
00143         black_number(tv, value.to_string());
00144 }
00145 
00146 
00147 void
00148 debugger_gnome::display_x_register(const number &value)
00149 {
00150     textview_number(x_reg_value, value);
00151 }
00152 
00153 
00154 void
00155 debugger_gnome::display_y_register(const number &value)
00156 {
00157     textview_number(y_reg_value, value);
00158 }
00159 
00160 
00161 void
00162 debugger_gnome::display_memory(int num, const number &value)
00163 {
00164     assert(num >= 1);
00165     assert(num <= 14);
00166     if (value == 0)
00167         memory_value[num - 1].get_buffer()->set_text("");
00168     else
00169         textview_number(memory_value[num - 1], value);
00170 }
00171 
00172 
00173 void
00174 debugger_gnome::display_address(int n)
00175 {
00176     address_value_changed_signal_enable = false;
00177     address_value.set_range((n ? 1 : 0), 240);
00178     address_value.set_value(n);
00179     address_value.update();  // needed?
00180     address_value_changed_signal_enable = true;
00181 }
00182 
00183 
00184 void
00185 debugger_gnome::on_address_value_changed(void)
00186 {
00187     if (address_value_changed_signal_enable)
00188     {
00189         int n = address_value.get_value_as_int();
00190         address_value_changed_signal_instance.emit(n);
00191     }
00192 }
00193 
00194 
00195 debugger_gnome::address_value_changed_signal_t &
00196 debugger_gnome::address_value_changed_signal(void)
00197 {
00198     return address_value_changed_signal_instance;
00199 }
00200 
00201 
00202 void
00203 debugger_gnome::display_opcode(opcode_t op)
00204 {
00205     std::string text = opcode_raw_name(op);
00206     if (opcode_valid(op))
00207         black_number(opcode_value, text + " " + ::opcode_name(op));
00208     else
00209         red_number(opcode_value, text);
00210 }
00211 
00212 
00213 static std::string
00214 basename(const std::string &filename)
00215 {
00216     const char *slash = strrchr(filename.c_str(), '/');
00217     if (!slash)
00218         return filename;
00219     return std::string(slash + 1);
00220 }
00221 
00222 
00223 void
00224 debugger_gnome::display_location(const location::pointer &loc)
00225 {
00226     if (!loc)
00227         return;
00228     if (filename != loc->get_filename())
00229     {
00230         // Maybe we could use a tabbed window and display more than one
00231         // source file, as needed.  Could happen if they use include
00232         // files (e.g. for standard subroutines).  However, experience
00233         // says this isn't a huge problem (the files are tiny compared
00234         // to modern source files).
00235         filename = loc->get_filename();
00236         Glib::RefPtr<Gtk::TextBuffer> tb = source_code.get_buffer();
00237         tb->set_text("");
00238 
00239         // read the entire contents of the file into the source code text view
00240         FILE *fp = fopen(filename.c_str(), "r");
00241         if (!fp)
00242         {
00243             tb->set_text(explain_fopen(filename.c_str(), "r"));
00244             return;
00245         }
00246 
00247         for (;;)
00248         {
00249             char buffer[4096];
00250             size_t n = fread(buffer, 1, sizeof(buffer), fp);
00251             if (!n)
00252                 break;
00253             tb->insert(tb->end(), buffer, buffer + n);
00254         }
00255         fclose(fp);
00256     }
00257 
00258     {
00259         std::string num = std_string_printf(": %d", loc->get_line_number());
00260         source_code_frame.set_label(basename(loc->get_filename()) + num);
00261     }
00262 
00263     // highlight the text
00264     Glib::RefPtr<Gtk::TextBuffer> tb = source_code.get_buffer();
00265     Gtk::TextIter lo = tb->get_iter_at_offset(loc->get_start());
00266     Gtk::TextIter hi = tb->get_iter_at_offset(loc->get_end());
00267     tb->place_cursor(hi);
00268     tb->select_range(lo, hi);
00269 
00270     // scroll the highlighted text onto the display
00271     source_code.scroll_to(lo);
00272 }
00273 
00274 
00275 void
00276 debugger_gnome::display_mstate(const char *text)
00277 {
00278     mstate_value.get_buffer()->set_text(text);
00279 }