//	bfe2 - control buttons
//	Copyright (c) 1999-2003 Brand Huntsman and Lee Salzman
//


#include "common.h"
#include "functions.h"


//////////////////////////////////////////////////////////////////////////

// global
GtkWidget *physical_text, *virtual_text, *instruction_text;
uint32 i_virtual_segment, i_virtual_offset, i_physical_address;
char i_bytes[LEN_INSTRUCTION], i_description[LEN_INSTRUCTION];

// local
GtkWidget *go_button, *stop_button, *step_button, *next_button;
uint i_size, enable_next, next_active;
uint32 next_physical;

//////////////////////////////////////////////////////////////////////////


void copy_instruction_string( char *buffer, char *string, const char delimiter ){
	char *end;
	uint i;

	end = string + LEN_INSTRUCTION;

	for(i = 0; string < end && buffer[i] != delimiter; i++, string++)
		*string = buffer[i];
	if(string == end) *(string-1) = '\0';
	else *string = '\0';

	return;
}


void set_ctrl_running( gboolean v ){
	if(v == TRUE){
		gtk_entry_set_text(GTK_ENTRY(physical_text), "");
		gtk_entry_set_text(GTK_ENTRY(virtual_text), "");
		gtk_entry_set_text(GTK_ENTRY(instruction_text), ".. r u n n i n g ..");

		gtk_widget_set_sensitive(stop_button, TRUE);
		v = FALSE;
	} else {
		gtk_widget_set_sensitive(stop_button, FALSE);
		v = TRUE;
	}

	gtk_widget_set_sensitive(go_button, v);
	gtk_widget_set_sensitive(step_button, v);
	if(enable_next) gtk_widget_set_sensitive(next_button, v);
	else gtk_widget_set_sensitive(next_button, FALSE);
	gtk_widget_set_sensitive(refreshall_button, v);

	breakpointsEnablePage(v);
	watchpointsEnablePage(v);
	memoryEnablePage(v);
	structuresEnablePage(v);
	prefsEnablePage(v);
}


void ctrl_go( GtkWidget *widget, gpointer data ){
	bochs_running = 1;

	set_ctrl_running(TRUE);

	fprintf(writepipe, "c\n");
	fgetc(readpipe);
	hook_sigio();

	historyWrite("GO...");
}


void ctrl_stop( GtkWidget *widget, gpointer data ){
	ctrlStop(0);
}


void ctrl_step( GtkWidget *widget, gpointer data ){
	fprintf(writepipe, "s\n");
	ctrlGetInstruction();
}


void ctrl_next( GtkWidget *widget, gpointer data ){
	next_physical = i_physical_address + i_size;
	fprintf(writepipe, "pb 0x%X\n", next_physical);
	#ifdef __DEBUG__
	 g_print("BFE DEBUG: (NEXT) pb 0x%X\n", next_physical);
	#endif
	prompt_read();

	next_active = 1;
	bochs_running = 1;

	set_ctrl_running(TRUE);

	fprintf(writepipe, "c\n");

	hook_sigio();

	historyWrite("NEXT...");
}


void read_instruction( ){
	char buffer[LEN_BUFFER], address[LEN_ADDRESS], *word;
	uint i, ii;

	enable_next = 0;
	gtk_widget_set_sensitive(next_button, FALSE);

	if(bochs_offline) return;

	i = 0;
	fgets(buffer, LEN_BUFFER, readpipe); // get "Next at t=1"
	fgets(buffer, LEN_BUFFER, readpipe); // get instruction

	// skip (0)
	if(buffer[i] == '(') goto_next_word(buffer, &i);

	// get physical address
	if(sscanf(&buffer[i], "[0x%x] ", &i_physical_address) != 1) return;
	goto_next_word(buffer, &i);

	// get virtual address
	if(sscanf(&buffer[i], "%x:%x ", &i_virtual_segment, &i_virtual_offset) != 2) return;
	goto_next_word(buffer, &i);

	// display physical and virtual addresses
	snprintf(address, LEN_ADDRESS, "%.8X", i_physical_address);
	gtk_entry_set_text(GTK_ENTRY(physical_text), address);
	snprintf(address, LEN_ADDRESS, "%.4X:%.8X", i_virtual_segment, i_virtual_offset);
	gtk_entry_set_text(GTK_ENTRY(virtual_text), address);

	// skip (unk. ctxt):
	if(buffer[i] == '('){
		goto_next_word(buffer, &i);
		goto_next_word(buffer, &i);
	}

	// get instruction
	copy_instruction_string(&buffer[i], i_description, ';');
	if(*i_description == '\0') return;

	ii = i;

	// enable NEXT button for special instructions
	get_word(buffer, &i, &word);
	if(string_compare(word, "call") || string_compare(word, "int")
	   || string_compare(word, "rep:") || string_compare(word, "loop")
	   || (word[0] == 'j' && !string_compare(word, "jmp"))){
		enable_next = 1;
		gtk_widget_set_sensitive(next_button, TRUE);
	}

	// get instruction size
	i = ii + strlen(i_description) + 2;
	sscanf(&buffer[i], "%*x%n", &i_size);
	i_size /= 2;
	copy_instruction_string(&buffer[i], i_bytes, '\n');
	goto_next_word(buffer, &i);

	// display instruction
	clip_whitespace(i_description);
	gtk_entry_set_text(GTK_ENTRY(instruction_text), i_description);

	return;
}


//////////////////////////////////////////////////////////////////////////


void ctrlInit( GtkWidget *box ){
	GtkWidget *hbox;

	hbox = new_hbox(box, FALSE);

	// physical address text field
	physical_text = new_text_entry(hbox, FALSE, WLEN_REGISTER);
	gtk_entry_set_editable(GTK_ENTRY(physical_text), FALSE);

	// virtual address text field
	virtual_text = new_text_entry(hbox, FALSE, WLEN_DTABLE);
	gtk_entry_set_editable(GTK_ENTRY(virtual_text), FALSE);

	// instruction text field
	instruction_text = new_text_entry(hbox, TRUE, 0);
	gtk_entry_set_editable(GTK_ENTRY(instruction_text), FALSE);

	// go button
	go_button = new_button(hbox, FALSE, " Go ");
	gtk_signal_connect(GTK_OBJECT(go_button), "clicked", GTK_SIGNAL_FUNC(ctrl_go), NULL);

	// stop button
	stop_button = new_button(hbox, FALSE, " Stop ");
	gtk_signal_connect(GTK_OBJECT(stop_button), "clicked", GTK_SIGNAL_FUNC(ctrl_stop), NULL);

	// step button
	step_button = new_button(hbox, FALSE, " Step ");
	gtk_signal_connect(GTK_OBJECT(step_button), "clicked", GTK_SIGNAL_FUNC(ctrl_step), NULL);

	// next button
	next_button = new_button(hbox, FALSE, " Next ");
	gtk_signal_connect(GTK_OBJECT(next_button), "clicked", GTK_SIGNAL_FUNC(ctrl_next), NULL);

	enable_next = 0;
	next_active = 0;
}


void ctrlStop( uint sigio ){
	char buffer[LEN_BUFFER], watch[10];
	uint bp, n, i, wp, wptype;

	unhook_sigio();
	bochs_running = 0;

	if(!sigio){
		// stop button was pressed, send ctrl-C to bochs
		kill(bochs_pid, SIGINT);
		ctrlGetInstruction();
	} else {
		// read breakpoint info
		fgets(buffer, LEN_BUFFER, readpipe);
		if(sscanf(buffer, "%*s Breakpoint %u,", &bp) == 1){
			ctrlGetInstruction();
			if(next_active && i_physical_address == next_physical){
				// delete breakpoint that was used by the NEXT button
				fprintf(writepipe, "d %u\n", bp);
				prompt_read();
				next_active = 0;
			} else {
				// encountered a normal breakpoint, mark in list
				historyWrite("Stopped on a breakpoint");
				breakpointsUpdate(BP_REDRAW);
				for(n = 0; n < MAX_BREAKPOINTS && breakpoints[n].enabled; n++)
					if(bp == breakpoints[n].number) break;
				breakpointsSelect(n);
			}
		} else if(sscanf(buffer, "%*s Caught %s watch point at %x", watch, &wp) == 2){
			ctrlGetInstruction();

			// encountered a watchpoint, mark in list
			historyWrite("Stopped on a watchpoint");
			watchpointsUpdate(BP_REDRAW);
			if(string_compare(watch, "read")) wptype = WP_READ;
			else wptype = WP_WRITE;
			for(n = 0; n < MAX_WATCHPOINTS && watchpoints[n].enabled; n++)
				if(wptype == watchpoints[n].type && wp == watchpoints[n].offset) break;
			watchpointsSelect(n);
		} else if(tracing){
			// tracing is on
			i = 0;
			if(buffer[i] == '(') goto_next_word(buffer, &i);
			sscanf(&buffer[i], "[0x%x] ", &i_physical_address);
			goto_next_word(buffer, &i);
			sscanf(&buffer[i], "%x:%x ", &i_virtual_segment, &i_virtual_offset);
			goto_next_word(buffer, &i);
			if(buffer[i] == '('){
				goto_next_word(buffer, &i);
				goto_next_word(buffer, &i);
			}
			copy_instruction_string(&buffer[i], i_description, ';');
			i += strlen(i_description) + 2;
			sscanf(&buffer[i], "%*x%n", &i_size);
			i_size /= 2;
			copy_instruction_string(&buffer[i], i_bytes, '\n');
			goto_next_word(buffer, &i);
			clip_whitespace(i_description);

			historyUpdate();
			bochs_running = 1;
			hook_sigio();
			return;
		} else {
			// dont know what happened here
		}
	}

	set_ctrl_running(FALSE);
}


void ctrlEnableControls( gboolean v ){
	if(v == FALSE){
		gtk_entry_set_text(GTK_ENTRY(physical_text), "");
		gtk_entry_set_text(GTK_ENTRY(virtual_text), "");
		gtk_entry_set_text(GTK_ENTRY(instruction_text), ".. o f f l i n e ..");

                gtk_widget_set_sensitive(stop_button, FALSE);
	}

	gtk_widget_set_sensitive(go_button, v);
	gtk_widget_set_sensitive(step_button, v);
	if(enable_next) gtk_widget_set_sensitive(next_button, v);
	else gtk_widget_set_sensitive(next_button, FALSE);
}


void ctrlGetInstruction( ){
	if(bochs_offline) return;

	#ifdef __DEBUG__
	 g_print("BFE DEBUG: (UPDATE) prompt = %u\n", p_step);
	#endif

	read_instruction();
	prompt_read();
	stateUpdate();
	historyUpdate();
}
