1 /*
2 * register.c
3 *
4 * Copyright 2013 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "common.h"
25
26 enum
27 {
28 REGISTER_PATH,
29 REGISTER_DISPLAY,
30 REGISTER_VALUE,
31 REGISTER_HB_MODE, /* natural register as char(s)?.. */
32 REGISTER_NAME,
33 REGISTER_ID,
34 REGISTER_FORMAT
35 };
36
37 static ScpTreeStore *store;
38 static GtkTreeSelection *selection;
39
40 enum
41 {
42 FORMAT_NATURAL,
43 FORMAT_HEX,
44 FORMAT_DECIMAL,
45 FORMAT_OCTAL,
46 FORMAT_BINARY,
47 FORMAT_RAW,
48 FORMAT_COUNT
49 };
50
51 static const char register_formats[FORMAT_COUNT] = { 'N', 'x', 'd', 'o', 't', 'r' };
52
register_iter_update(GtkTreeIter * iter,GString * commands[])53 static void register_iter_update(GtkTreeIter *iter, GString *commands[])
54 {
55 gint id, format;
56
57 scp_tree_store_get(store, iter, REGISTER_ID, &id, REGISTER_FORMAT, &format, -1);
58 g_string_append_printf(commands[format], " %d", id);
59 }
60
register_node_update(const ParseNode * node,GString * commands[])61 static void register_node_update(const ParseNode *node, GString *commands[])
62 {
63 iff (node->type == PT_VALUE, "changed-registers: contains array")
64 {
65 const char *id = (const char *) node->value;
66 GtkTreeIter iter;
67
68 iff (store_find(store, &iter, REGISTER_ID, id), "%s: rid not found", id)
69 register_iter_update(&iter, commands);
70 }
71 }
72
73 static gboolean query_all_registers = TRUE;
74
registers_send_update(GArray * nodes,char token)75 static void registers_send_update(GArray *nodes, char token)
76 {
77 GString *commands[FORMAT_COUNT];
78 guint format;
79 gsize empty;
80
81 for (format = 0; format < FORMAT_COUNT; format++)
82 {
83 commands[format] = g_string_sized_new(0x7F);
84 g_string_append_printf(commands[format], "0%c9%c%s%s-data-list-register-values %c",
85 token, FRAME_ARGS, register_formats[format]);
86 }
87
88 empty = commands[FORMAT_NATURAL]->len;
89
90 if (nodes)
91 parse_foreach(nodes, (GFunc) register_node_update, commands);
92 else
93 {
94 store_foreach(store, (GFunc) register_iter_update, commands);
95 query_all_registers = FALSE;
96 }
97
98 for (format = 0; format < FORMAT_COUNT; format++)
99 {
100 if (commands[format]->len > empty)
101 debug_send_command(F, commands[format]->str);
102
103 g_string_free(commands[format], TRUE);
104 }
105 }
106
107 typedef struct _IndexData
108 {
109 guint index;
110 guint count;
111 } IndexData;
112
register_node_name(const ParseNode * node,IndexData * id)113 static void register_node_name(const ParseNode *node, IndexData *id)
114 {
115 iff (node->type == PT_VALUE, "register-names: contains array")
116 {
117 const char *name = (const char *) node->value;
118
119 if (*name)
120 {
121 GtkTreeIter iter, iter1;
122
123 if (store_find(store, &iter1, REGISTER_NAME, name))
124 {
125 scp_tree_store_iter_nth_child(store, &iter, NULL, id->count);
126 scp_tree_store_swap(store, &iter, &iter1);
127 }
128 else
129 {
130 scp_tree_store_insert_with_values(store, &iter, NULL, id->count,
131 REGISTER_PATH, name, REGISTER_NAME, name, REGISTER_HB_MODE,
132 HB_DEFAULT, REGISTER_FORMAT, FORMAT_NATURAL, -1);
133 }
134
135 scp_tree_store_set(store, &iter, REGISTER_DISPLAY, NULL, REGISTER_VALUE,
136 NULL, REGISTER_ID, id->index, -1);
137 id->count++;
138 }
139
140 id->index++;
141 }
142 }
143
on_register_names(GArray * nodes)144 void on_register_names(GArray *nodes)
145 {
146 IndexData id = { 0, 0 };
147 gboolean valid;
148 GtkTreeIter iter;
149 const char *token = parse_grab_token(nodes);
150
151 parse_foreach(parse_lead_array(nodes), (GFunc) register_node_name, &id);
152 valid = scp_tree_store_iter_nth_child(store, &iter, NULL, id.count);
153 while (valid)
154 valid = scp_tree_store_remove(store, &iter);
155
156 if (token)
157 registers_send_update(NULL, '2');
158 }
159
on_register_changes(GArray * nodes)160 void on_register_changes(GArray *nodes)
161 {
162 const char *token = parse_grab_token(nodes);
163 GArray *changes = parse_lead_array(nodes);
164
165 if (token)
166 {
167 if (utils_matches_frame(token))
168 registers_send_update(changes, '4');
169 }
170 else if (changes->len)
171 query_all_registers = TRUE; /* external changes */
172 }
173
register_set_value(GtkTreeIter * iter,char * value)174 static void register_set_value(GtkTreeIter *iter, char *value)
175 {
176 if (*value == '{')
177 {
178 const char *parent;
179 GtkTreeIter child;
180 gboolean valid = scp_tree_store_iter_children(store, &child, iter);
181 gboolean next;
182
183 scp_tree_store_set(store, iter, REGISTER_DISPLAY, NULL, REGISTER_VALUE, NULL, -1);
184 scp_tree_store_get(store, iter, REGISTER_NAME, &parent, -1);
185
186 do
187 {
188 const char *name = ++value;
189 char *path, *end;
190
191 if ((value = strchr(name, '=')) == NULL)
192 {
193 dc_error("= expected");
194 break;
195 }
196
197 value[-(isspace(value[-1]) != 0)] = '\0';
198
199 if (!*name)
200 {
201 dc_error("name expected");
202 break;
203 }
204
205 if (isspace(*++value))
206 value++;
207
208 if (*value == '{')
209 {
210 if ((end = strchr(value, '}')) != NULL)
211 end++;
212 }
213 else if ((end = strchr(value, ',')) == NULL)
214 end = strchr(value, '}');
215
216 if (!end)
217 {
218 dc_error(", or } expected");
219 break;
220 }
221
222 next = *end == ',';
223 *end = '\0';
224 path = g_strdup_printf("%s.%s", parent, name);
225
226 if (!valid)
227 scp_tree_store_append(store, &child, iter);
228 scp_tree_store_set(store, &child, REGISTER_PATH, path, REGISTER_NAME, name,
229 REGISTER_DISPLAY, value, REGISTER_VALUE, value, -1);
230 valid &= scp_tree_store_iter_next(store, &child);
231
232 g_free(path);
233 value = end;
234 if (isspace(value[1]))
235 value++;
236
237 } while (next);
238
239 while (valid)
240 valid = scp_tree_store_remove(store, &child);
241 }
242 else
243 {
244 scp_tree_store_clear_children(store, iter, FALSE);
245 scp_tree_store_set(store, iter, REGISTER_DISPLAY, value, REGISTER_VALUE, value, -1);
246 }
247 }
248
249 typedef struct _ValueAction
250 {
251 gint format;
252 gboolean assign;
253 } ValueAction;
254
register_node_value(const ParseNode * node,const ValueAction * va)255 static void register_node_value(const ParseNode *node, const ValueAction *va)
256 {
257 iff (node->type == PT_ARRAY, "register-values: contains value")
258 {
259 GArray *nodes = (GArray *) node->value;
260 const char *number = parse_find_value(nodes, "number");
261 char *value = parse_find_value(nodes, "value");
262
263 iff (number && value, "no number or value")
264 {
265 GtkTreeIter iter;
266
267 if (store_find(store, &iter, REGISTER_ID, number), "%s: rid not found")
268 {
269 if (va->format < FORMAT_COUNT)
270 scp_tree_store_set(store, &iter, REGISTER_FORMAT, va->format, -1);
271
272 if (va->assign)
273 register_set_value(&iter, value);
274 }
275 }
276 }
277 }
278
on_register_values(GArray * nodes)279 void on_register_values(GArray *nodes)
280 {
281 const char *token = parse_grab_token(nodes);
282 ValueAction va = { *token - '0', utils_matches_frame(token + 1) };
283
284 if (va.format < FORMAT_COUNT || va.assign)
285 parse_foreach(parse_lead_array(nodes), (GFunc) register_node_value, &va);
286 }
287
on_register_display_edited(G_GNUC_UNUSED GtkCellRendererText * renderer,gchar * path_str,gchar * new_text,G_GNUC_UNUSED gpointer gdata)288 static void on_register_display_edited(G_GNUC_UNUSED GtkCellRendererText *renderer,
289 gchar *path_str, gchar *new_text, G_GNUC_UNUSED gpointer gdata)
290 {
291 view_display_edited(store, debug_state() & DS_DEBUG, path_str, "07-gdb-set var $%s=%s",
292 new_text);
293 }
294
295 static const TreeCell register_cells[] =
296 {
297 { "register_display", G_CALLBACK(on_register_display_edited) },
298 { NULL, NULL }
299 };
300
register_iter_clear(GtkTreeIter * iter,G_GNUC_UNUSED gpointer gdata)301 static void register_iter_clear(GtkTreeIter *iter, G_GNUC_UNUSED gpointer gdata)
302 {
303 if (scp_tree_store_iter_has_child(store, iter))
304 scp_tree_store_clear_children(store, iter, FALSE);
305 else
306 scp_tree_store_set(store, iter, REGISTER_DISPLAY, NULL, REGISTER_VALUE, NULL, -1);
307 }
308
registers_clear(void)309 void registers_clear(void)
310 {
311 store_foreach(store, (GFunc) register_iter_clear, NULL);
312 query_all_registers = TRUE;
313 }
314
registers_update(void)315 gboolean registers_update(void)
316 {
317 if (view_frame_update())
318 return FALSE;
319
320 if (frame_id)
321 {
322 if (query_all_registers)
323 registers_send_update(NULL, '4');
324 else
325 debug_send_format(F, "04%c%s%s-data-list-changed-registers", FRAME_ARGS);
326 }
327 else
328 registers_clear();
329
330 return TRUE;
331 }
332
333 static char *last_gdb_executable = NULL;
334
registers_query_names(void)335 void registers_query_names(void)
336 {
337 if (g_strcmp0(pref_gdb_executable, last_gdb_executable))
338 {
339 g_free(last_gdb_executable);
340 last_gdb_executable = g_strdup(pref_gdb_executable);
341 debug_send_command(N, "-data-list-register-names");
342 }
343 }
344
345 static GtkWidget *tree;
346
registers_show(gboolean show)347 void registers_show(gboolean show)
348 {
349 gtk_widget_set_visible(tree, show);
350 }
351
352 static GObject *register_display;
353
registers_update_state(DebugState state)354 void registers_update_state(DebugState state)
355 {
356 GtkTreeIter iter;
357
358 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
359 {
360 GtkTreeIter parent;
361
362 g_object_set(register_display, "editable", (state & DS_DEBUG) &&
363 (scp_tree_store_iter_parent(store, &parent, &iter) ||
364 !scp_tree_store_iter_has_child(store, &iter)), NULL);
365 }
366 }
367
registers_delete_all(void)368 void registers_delete_all(void)
369 {
370 store_clear(store);
371 }
372
register_load(GKeyFile * config,const char * section)373 static gboolean register_load(GKeyFile *config, const char *section)
374 {
375 char *name = utils_key_file_get_string(config, section, "name");
376 gint format = utils_get_setting_integer(config, section, "format", FORMAT_NATURAL);
377 gboolean valid = FALSE;
378
379 if (name && (unsigned) format < FORMAT_COUNT)
380 {
381 scp_tree_store_append_with_values(store, NULL, NULL, REGISTER_PATH, name,
382 REGISTER_NAME, name, REGISTER_HB_MODE, HB_DEFAULT, REGISTER_FORMAT,
383 format, -1);
384 valid = TRUE;
385 }
386
387 g_free(name);
388 return valid;
389 }
390
registers_load(GKeyFile * config)391 void registers_load(GKeyFile *config)
392 {
393 registers_delete_all();
394 g_free(last_gdb_executable);
395 last_gdb_executable = NULL;
396 utils_load(config, "register", register_load);
397 }
398
register_save(GKeyFile * config,const char * section,GtkTreeIter * iter)399 static gboolean register_save(GKeyFile *config, const char *section, GtkTreeIter *iter)
400 {
401 const char *name;
402 gint format;
403
404 scp_tree_store_get(store, iter, REGISTER_NAME, &name, REGISTER_FORMAT, &format, -1);
405
406 if (format == FORMAT_NATURAL)
407 return FALSE;
408
409 g_key_file_set_string(config, section, "name", name);
410 g_key_file_set_integer(config, section, "format", format);
411 return TRUE;
412 }
413
registers_save(GKeyFile * config)414 void registers_save(GKeyFile *config)
415 {
416 store_save(store, config, "register", register_save);
417 }
418
on_register_query_tooltip(G_GNUC_UNUSED GtkWidget * widget,gint x,gint y,gboolean keyboard_tip,GtkTooltip * tooltip,GtkTreeViewColumn * register_name_column)419 static gboolean on_register_query_tooltip(G_GNUC_UNUSED GtkWidget *widget, gint x, gint y,
420 gboolean keyboard_tip, GtkTooltip *tooltip, GtkTreeViewColumn *register_name_column)
421 {
422 GtkTreeView *tree = GTK_TREE_VIEW(widget);
423 GtkTreeIter iter;
424
425 if (gtk_tree_view_get_tooltip_context(tree, &x, &y, keyboard_tip, NULL, NULL, &iter))
426 {
427 gint id;
428 char *text;
429
430 gtk_tree_view_set_tooltip_cell(tree, tooltip, NULL, register_name_column, NULL);
431 scp_tree_store_get(store, &iter, REGISTER_ID, &id, -1);
432 text = g_strdup_printf("register %d", id);
433 gtk_tooltip_set_text(tooltip, text);
434 g_free(text);
435 return TRUE;
436 }
437
438 return FALSE;
439 }
440
on_register_refresh(G_GNUC_UNUSED const MenuItem * menu_item)441 static void on_register_refresh(G_GNUC_UNUSED const MenuItem *menu_item)
442 {
443 registers_send_update(NULL, '2');
444 }
445
on_register_copy(const MenuItem * menu_item)446 static void on_register_copy(const MenuItem *menu_item)
447 {
448 menu_copy(selection, menu_item);
449 }
450
on_register_format_display(const MenuItem * menu_item)451 static void on_register_format_display(const MenuItem *menu_item)
452 {
453 menu_mode_display(selection, menu_item, REGISTER_FORMAT);
454 }
455
on_register_format_update(const MenuItem * menu_item)456 static void on_register_format_update(const MenuItem *menu_item)
457 {
458 GtkTreeIter iter;
459 gint format = GPOINTER_TO_INT(menu_item->gdata);
460 gint id;
461
462 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
463 {
464 scp_tree_store_get(store, &iter, REGISTER_ID, &id, -1);
465
466 if (debug_state() & DS_DEBUG)
467 {
468 debug_send_format(N, "02%d%c%s%s-data-list-register-values %c %d", format,
469 FRAME_ARGS, register_formats[format], id);
470 }
471 else
472 {
473 scp_tree_store_set(store, &iter, REGISTER_FORMAT, format, -1);
474 }
475 }
476 }
477
on_register_query(G_GNUC_UNUSED const MenuItem * menu_item)478 static void on_register_query(G_GNUC_UNUSED const MenuItem *menu_item)
479 {
480 debug_send_command(N, debug_state() & DS_DEBUG ? "02-data-list-register-names" :
481 "-data-list-register-names");
482 }
483
484 #define DS_FRESHABLE DS_DEBUG
485 #define DS_COPYABLE (DS_BASICS | DS_EXTRA_1)
486 #define DS_FORMATABLE (DS_INACTIVE | DS_DEBUG | DS_EXTRA_2)
487 #define DS_QUERABLE DS_SENDABLE
488
489 #define FORMAT_ITEM(format, FORMAT) \
490 { ("register_format_"format), on_register_format_update, DS_FORMATABLE, NULL, \
491 GINT_TO_POINTER(FORMAT) }
492
493 static MenuItem register_menu_items[] =
494 {
495 { "register_refresh", on_register_refresh, DS_FRESHABLE, NULL, NULL },
496 { "register_copy", on_register_copy, DS_COPYABLE, NULL, NULL },
497 { "register_format", on_register_format_display, DS_FORMATABLE, NULL, NULL },
498 FORMAT_ITEM("natural", FORMAT_NATURAL),
499 FORMAT_ITEM("hex", FORMAT_HEX),
500 FORMAT_ITEM("decimal", FORMAT_DECIMAL),
501 FORMAT_ITEM("octal", FORMAT_OCTAL),
502 FORMAT_ITEM("binary", FORMAT_BINARY),
503 FORMAT_ITEM("raw", FORMAT_RAW),
504 { "register_query", on_register_query, DS_QUERABLE, NULL, NULL },
505 { NULL, NULL, 0, NULL, NULL }
506 };
507
register_menu_extra_state(void)508 static guint register_menu_extra_state(void)
509 {
510 GtkTreeIter iter;
511
512 if (gtk_tree_selection_get_selected(selection, NULL, &iter))
513 {
514 GtkTreeIter parent;
515
516 return (1 << DS_INDEX_1) |
517 (!scp_tree_store_iter_parent(store, &parent, &iter) << DS_INDEX_2);
518 }
519
520 return 0;
521 }
522
523 static MenuInfo register_menu_info = { register_menu_items, register_menu_extra_state, 0 };
524
on_register_selection_changed(G_GNUC_UNUSED GtkTreeSelection * selection,G_GNUC_UNUSED gpointer gdata)525 static void on_register_selection_changed(G_GNUC_UNUSED GtkTreeSelection *selection,
526 G_GNUC_UNUSED gpointer gdata)
527 {
528 registers_update_state(debug_state());
529 }
530
register_init(void)531 void register_init(void)
532 {
533 tree = GTK_WIDGET(view_connect("register_view", &store, &selection, register_cells,
534 "register_window", ®ister_display));
535 gtk_widget_set_has_tooltip(tree, TRUE);
536 g_signal_connect(tree, "query-tooltip", G_CALLBACK(on_register_query_tooltip),
537 get_column("register_name_column"));
538 menu_select("register_menu", ®ister_menu_info, selection);
539 g_signal_connect(selection, "changed", G_CALLBACK(on_register_selection_changed), NULL);
540 }
541
registers_finalize(void)542 void registers_finalize(void)
543 {
544 g_free(last_gdb_executable);
545 }
546