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", &register_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", &register_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