//	bfe2 - cpu state
//	Copyright (c) 1999-2003 Brand Huntsman and Lee Salzman
//


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


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

// global
s_cpu cpu;

// local
GtkWidget *r_eax_text, *r_ebx_text, *r_ecx_text, *r_edx_text;
GtkWidget *r_ebp_text, *r_esi_text, *r_edi_text, *r_esp_text;
GtkWidget *r_eflags_text, *r_eip_text, *stack_label;
GtkWidget *r_cs_text, *r_ss_text, *r_ds_text, *r_es_text, *r_fs_text, *r_gs_text;
GtkWidget *r_tr_text, *r_ldtr_text, *r_gdtr_text, *r_idtr_text;
GtkWidget *r_dr0_text, *r_dr1_text, *r_dr2_text, *r_dr3_text;
GtkWidget *r_dr4_text, *r_dr5_text, *r_dr6_text, *r_dr7_text;
GtkWidget *r_tr3_text, *r_tr4_text, *r_tr5_text, *r_tr6_text, *r_tr7_text;
GtkWidget *r_cr0_text, *r_cr1_text, *r_cr2_text, *r_cr3_text, *r_cr4_text;
GtkCList *stack_list, *eflags_list;

#define STACK_COLUMN_TITLES 3
gchar *stack_column_titles[] = { "ESP", "Value", "EBP" };
#define EFLAGS_COLUMN_TITLES 2
gchar *eflags_column_titles[] = { "", "" };

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


static GtkWidget *new_vbox_1( GtkWidget *box ){
	GtkWidget *vbox;

	vbox = gtk_vbox_new(FALSE, 1);
	gtk_box_pack_start(GTK_BOX(box), vbox, FALSE, FALSE, 0);
	gtk_widget_show(vbox);

	return(vbox);
}


static GtkWidget *new_label_and_entry( GtkWidget *box, char *name, uint width ){
	GtkWidget *hbox, *text;

	hbox = new_hbox(box, FALSE);

	new_label(hbox, TRUE, name, FALSE);

	text = new_text_entry(hbox, FALSE, width);
	gtk_entry_set_editable(GTK_ENTRY(text), FALSE);

	return(text);
}


static GtkCList *new_sized_list( GtkWidget *box, uint columns, gchar *titles[], uint width, uint height ){
	GtkWidget *scrolled_window;
	GtkCList *list;
	uint x;

	// create scrolled window
	scrolled_window = gtk_scrolled_window_new(NULL, NULL);
	gtk_widget_set_usize(scrolled_window, width, height);
	gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 0);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
		GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_box_pack_start(GTK_BOX(box), scrolled_window, TRUE, TRUE, 0);
	gtk_widget_show(scrolled_window);

	// create list
	list = (GtkCList *)gtk_clist_new_with_titles(columns, titles);
	for(x = 0; x < columns; x++)
		gtk_clist_set_column_auto_resize(list, x, TRUE);
	gtk_clist_set_selection_mode(list, GTK_SELECTION_SINGLE);
	gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(list));
	gtk_widget_show(GTK_WIDGET(list));

	gtk_clist_column_titles_hide(list);

	return(list);
}


uint virtual_to_physical( s_segment *segment, uint32 offset , uint32 *physical ){
	uint32 limit, base;

	if(!segment->valid) return(0);

	limit = (segment->descriptor[0] & 0xffff) + (segment->descriptor[1] & 0xf0000);
	if((segment->descriptor[1] & 0x400000) != 0) limit = limit << 12;

	base = (segment->descriptor[0] >> 16) + ((segment->descriptor[1] & 0xff) << 16);
	base += segment->descriptor[1] & 0xff000000;

	*physical = base + offset;

	if((base + offset) > (base + limit)) return(0);
	return(1);
}


void read_state( ){
	if(bochs_offline || bochs_running) return;

	fprintf(writepipe, "dump_cpu\n");

	fscanf(readpipe,
		" eax:%x\nebx:%x\necx:%x\nedx:%x\n"
		"ebp:%x\nesi:%x\nedi:%x\nesp:%x\n"
		"eflags:%x\neip:%x\n"
		"cs:s=%x, dl=%x, dh=%x, valid=%u\n"
		"ss:s=%x, dl=%x, dh=%x, valid=%u\n"
		"ds:s=%x, dl=%x, dh=%x, valid=%u\n"
		"es:s=%x, dl=%x, dh=%x, valid=%u\n"
		"fs:s=%x, dl=%x, dh=%x, valid=%u\n"
		"gs:s=%x, dl=%x, dh=%x, valid=%u\n"
		"ldtr:s=%x, dl=%x, dh=%x, valid=%u\n"
		"tr:s=%x, dl=%x, dh=%x, valid=%u\n"
		"gdtr:base=%x, limit=%x\n"
		"idtr:base=%x, limit=%x\n"
		"dr0:%x\ndr1:%x\ndr2:%x\ndr3:%x\n"
//		"dr4:%x\ndr5:%x\n"
		"dr6:%x\ndr7:%x\n"
		"tr3:%x\ntr4:%x\ntr5:%x\ntr6:%x\ntr7:%x\n"
		"cr0:%x\ncr1:%x\ncr2:%x\ncr3:%x\ncr4:%x\n"
		"inhibit_mask:%u\ndone\n",
		&cpu.eax, &cpu.ebx, &cpu.ecx, &cpu.edx,
		&cpu.ebp, &cpu.esi, &cpu.edi, &cpu.esp,
		&cpu.eflags, &cpu.eip,
		&cpu.cs.segment, &cpu.cs.descriptor[0], &cpu.cs.descriptor[1], &cpu.cs.valid,
		&cpu.ss.segment, &cpu.ss.descriptor[0], &cpu.ss.descriptor[1], &cpu.ss.valid,
		&cpu.ds.segment, &cpu.ds.descriptor[0], &cpu.ds.descriptor[1], &cpu.ds.valid,
		&cpu.es.segment, &cpu.es.descriptor[0], &cpu.es.descriptor[1], &cpu.es.valid,
		&cpu.fs.segment, &cpu.fs.descriptor[0], &cpu.fs.descriptor[1], &cpu.fs.valid,
		&cpu.gs.segment, &cpu.gs.descriptor[0], &cpu.gs.descriptor[1], &cpu.gs.valid,
		&cpu.ldtr.segment, &cpu.ldtr.descriptor[0], &cpu.ldtr.descriptor[1], &cpu.ldtr.valid,
		&cpu.tr.segment, &cpu.tr.descriptor[0], &cpu.tr.descriptor[1], &cpu.tr.valid,
		&cpu.gdtr.base, &cpu.gdtr.limit,
		&cpu.idtr.base, &cpu.idtr.limit,
		&cpu.dr0, &cpu.dr1, &cpu.dr2, &cpu.dr3,
//		&cpu.dr4, &cpu.dr5,
		&cpu.dr6, &cpu.dr7,
		&cpu.tr3, &cpu.tr4, &cpu.tr5, &cpu.tr6, &cpu.tr7,
		&cpu.cr0, &cpu.cr1, &cpu.cr2, &cpu.cr3, &cpu.cr4,
		&cpu.inhibit);

	prompt_read();
}


void add_flag( uint value, char *name ){
	char temp[2], *row[EFLAGS_COLUMN_TITLES];

	snprintf(temp, 2, "%u", value);
	row[0] = temp;
	row[1] = name;
	gtk_clist_append(eflags_list, row);
}


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


void stateInit( GtkWidget *hbox ){
	GtkWidget *vbox;

	vbox = new_vbox_1(hbox);

	stack_label = new_label(vbox, TRUE, "Stack", FALSE);
	stack_list = new_sized_list(vbox, STACK_COLUMN_TITLES, stack_column_titles, STACK_LIST_W, STACK_LIST_H);
	gtk_clist_column_titles_show(stack_list);

	r_dr0_text = new_label_and_entry(vbox, "DR0", WLEN_REGISTER);
	r_dr1_text = new_label_and_entry(vbox, "DR1", WLEN_REGISTER);
	r_dr2_text = new_label_and_entry(vbox, "DR2", WLEN_REGISTER);
	r_dr3_text = new_label_and_entry(vbox, "DR3", WLEN_REGISTER);
//	r_dr4_text = new_label_and_entry(vbox, "DR4", WLEN_REGISTER);
//	r_dr5_text = new_label_and_entry(vbox, "DR5", WLEN_REGISTER);
	r_dr6_text = new_label_and_entry(vbox, "DR6", WLEN_REGISTER);
	r_dr7_text = new_label_and_entry(vbox, "DR7", WLEN_REGISTER);

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

	vbox = new_vbox_1(hbox);

	r_eax_text = new_label_and_entry(vbox, "EAX", WLEN_REGISTER);
	r_ebx_text = new_label_and_entry(vbox, "EBX", WLEN_REGISTER);
	r_ecx_text = new_label_and_entry(vbox, "ECX", WLEN_REGISTER);
	r_edx_text = new_label_and_entry(vbox, "EDX", WLEN_REGISTER);
	r_ebp_text = new_label_and_entry(vbox, "EBP", WLEN_REGISTER);
	r_esi_text = new_label_and_entry(vbox, "ESI", WLEN_REGISTER);
	r_edi_text = new_label_and_entry(vbox, "EDI", WLEN_REGISTER);
	r_esp_text = new_label_and_entry(vbox, "ESP", WLEN_REGISTER);

	new_gap(vbox);

	r_tr3_text = new_label_and_entry(vbox, "TR3", WLEN_REGISTER);
	r_tr4_text = new_label_and_entry(vbox, "TR4", WLEN_REGISTER);
	r_tr5_text = new_label_and_entry(vbox, "TR5", WLEN_REGISTER);
	r_tr6_text = new_label_and_entry(vbox, "TR6", WLEN_REGISTER);
	r_tr7_text = new_label_and_entry(vbox, "TR7", WLEN_REGISTER);

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

	vbox = new_vbox_1(hbox);

	r_eflags_text = new_label_and_entry(vbox, "EFLAGS", WLEN_REGISTER);
	r_eip_text = new_label_and_entry(vbox, "EIP", WLEN_REGISTER);
	r_cs_text = new_label_and_entry(vbox, "CS", WLEN_SEGMENT);
	r_ss_text = new_label_and_entry(vbox, "SS", WLEN_SEGMENT);
	r_ds_text = new_label_and_entry(vbox, "DS", WLEN_SEGMENT);
	r_es_text = new_label_and_entry(vbox, "ES", WLEN_SEGMENT);
	r_fs_text = new_label_and_entry(vbox, "FS", WLEN_SEGMENT);
	r_gs_text = new_label_and_entry(vbox, "GS", WLEN_SEGMENT);

	new_gap(vbox);

	r_cr0_text = new_label_and_entry(vbox, "CR0", WLEN_REGISTER);
	r_cr1_text = new_label_and_entry(vbox, "CR1", WLEN_REGISTER);
	r_cr2_text = new_label_and_entry(vbox, "CR2", WLEN_REGISTER);
	r_cr3_text = new_label_and_entry(vbox, "CR3", WLEN_REGISTER);
	r_cr4_text = new_label_and_entry(vbox, "CR4", WLEN_REGISTER);

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

	vbox = new_vbox_1(hbox);

	r_tr_text = new_label_and_entry(vbox, "TR", WLEN_SEGMENT);
	r_ldtr_text = new_label_and_entry(vbox, "LDTR", WLEN_SEGMENT);
	r_gdtr_text = new_label_and_entry(vbox, "GDTR", WLEN_DTABLE);
	r_idtr_text = new_label_and_entry(vbox, "IDTR", WLEN_DTABLE);

	eflags_list = new_sized_list(vbox, EFLAGS_COLUMN_TITLES, eflags_column_titles, FLAGS_LIST_W, FLAGS_LIST_H);
}


#define LEN_TEMP 50
void stateUpdate( ){
	char temp[LEN_TEMP];

	read_state();

	if(!show_state) return;

	snprintf(temp, LEN_TEMP, "%.8X", cpu.eax);
	gtk_entry_set_text(GTK_ENTRY(r_eax_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.ebx);
	gtk_entry_set_text(GTK_ENTRY(r_ebx_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.ecx);
	gtk_entry_set_text(GTK_ENTRY(r_ecx_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.edx);
	gtk_entry_set_text(GTK_ENTRY(r_edx_text), temp);
	if(((cpu.cr0 & 1) && (cpu.ss.descriptor[1] & 0x400000)) || (cpu.ebp > 0xffff))
		snprintf(temp, LEN_TEMP, "%.8X", cpu.ebp);
	else snprintf(temp, LEN_TEMP, "%.4X", cpu.ebp);
	gtk_entry_set_text(GTK_ENTRY(r_ebp_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.esi);
	gtk_entry_set_text(GTK_ENTRY(r_esi_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.edi);
	gtk_entry_set_text(GTK_ENTRY(r_edi_text), temp);
	if(((cpu.cr0 & 1) && (cpu.ss.descriptor[1] & 0x400000)) || (cpu.esp > 0xffff))
		snprintf(temp, LEN_TEMP, "%.8X", cpu.esp);
	else snprintf(temp, LEN_TEMP, "%.4X", cpu.esp);
	gtk_entry_set_text(GTK_ENTRY(r_esp_text), temp);

	snprintf(temp, LEN_TEMP, "%.8X", cpu.eflags);
	gtk_entry_set_text(GTK_ENTRY(r_eflags_text), temp);
	if(((cpu.cr0 & 1) && (cpu.cs.descriptor[1] & 0x400000)) || (cpu.eip > 0xffff))
		snprintf(temp, LEN_TEMP, "%.8X", cpu.eip);
	else snprintf(temp, LEN_TEMP, "%.4X", cpu.eip);
	gtk_entry_set_text(GTK_ENTRY(r_eip_text), temp);

	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.cs.segment, (cpu.cs.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_cs_text), temp);
	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.ss.segment, (cpu.ss.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_ss_text), temp);
	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.ds.segment, (cpu.ds.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_ds_text), temp);
	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.es.segment, (cpu.es.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_es_text), temp);
	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.fs.segment, (cpu.fs.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_fs_text), temp);
	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.gs.segment, (cpu.gs.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_gs_text), temp);

	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.tr.segment, (cpu.tr.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_tr_text), temp);
	snprintf(temp, LEN_TEMP, "%.4X  %s", cpu.ldtr.segment, (cpu.ldtr.valid ? "valid" : "invalid"));
	gtk_entry_set_text(GTK_ENTRY(r_ldtr_text), temp);

	snprintf(temp, LEN_TEMP, "%.8X  (%.5X)", cpu.gdtr.base, cpu.gdtr.limit);
	gtk_entry_set_text(GTK_ENTRY(r_gdtr_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X  (%.5X)", cpu.idtr.base, cpu.idtr.limit);
	gtk_entry_set_text(GTK_ENTRY(r_idtr_text), temp);

	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr0);
	gtk_entry_set_text(GTK_ENTRY(r_dr0_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr1);
	gtk_entry_set_text(GTK_ENTRY(r_dr1_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr2);
	gtk_entry_set_text(GTK_ENTRY(r_dr2_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr3);
	gtk_entry_set_text(GTK_ENTRY(r_dr3_text), temp);
//	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr4);
//	gtk_entry_set_text(GTK_ENTRY(r_dr4_text), temp);
//	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr5);
//	gtk_entry_set_text(GTK_ENTRY(r_dr5_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr6);
	gtk_entry_set_text(GTK_ENTRY(r_dr6_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.dr7);
	gtk_entry_set_text(GTK_ENTRY(r_dr7_text), temp);

	snprintf(temp, LEN_TEMP, "%.8X", cpu.tr3);
	gtk_entry_set_text(GTK_ENTRY(r_tr3_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.tr4);
	gtk_entry_set_text(GTK_ENTRY(r_tr4_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.tr5);
	gtk_entry_set_text(GTK_ENTRY(r_tr5_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.tr6);
	gtk_entry_set_text(GTK_ENTRY(r_tr6_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.tr7);
	gtk_entry_set_text(GTK_ENTRY(r_tr7_text), temp);

	snprintf(temp, LEN_TEMP, "%.8X", cpu.cr0);
	gtk_entry_set_text(GTK_ENTRY(r_cr0_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.cr1);
	gtk_entry_set_text(GTK_ENTRY(r_cr1_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.cr2);
	gtk_entry_set_text(GTK_ENTRY(r_cr2_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.cr3);
	gtk_entry_set_text(GTK_ENTRY(r_cr3_text), temp);
	snprintf(temp, LEN_TEMP, "%.8X", cpu.cr4);
	gtk_entry_set_text(GTK_ENTRY(r_cr4_text), temp);

	gtk_clist_freeze(eflags_list);
	gtk_clist_clear(eflags_list);
	add_flag(cpu.eflags&1, "Carry");
	add_flag((cpu.eflags&2)>>1, "-");			// 1
	add_flag((cpu.eflags&4)>>2, "Parity");
	add_flag((cpu.eflags&8)>>3, "-");			// 0
	add_flag((cpu.eflags&16)>>4, "Auxillary");
	add_flag((cpu.eflags&32)>>5, "-");			// 0
	add_flag((cpu.eflags&64)>>6, "Zero");
	add_flag((cpu.eflags&128)>>7, "Sign");
	add_flag((cpu.eflags&256)>>8, "Trap");
	add_flag((cpu.eflags&512)>>9, "Interrupt");
	add_flag((cpu.eflags&1024)>>10, "Direction");
	add_flag((cpu.eflags&2048)>>11, "Overflow");
	add_flag((cpu.eflags&12288)>>12, "IOPL");
	add_flag((cpu.eflags&16384)>>14, "Nested Task");
	add_flag((cpu.eflags&32768)>>15, "-");			// 0
	add_flag((cpu.eflags&65535)>>16, "Resume");
	add_flag((cpu.eflags&131072)>>17, "V86");
	add_flag((cpu.eflags&262144)>>18, "Alignment Check");
	add_flag((cpu.eflags&524288)>>19, "Virtual Interrupt");
	add_flag((cpu.eflags&1048576)>>20, "Virtual Interrupt Pending");
	add_flag((cpu.eflags&2097152)>>21, "ID");
	gtk_clist_thaw(eflags_list);

	stateUpdateStack();
}


void stateUpdateStack( ){
	char esp_offset[LEN_NUMBER], value[LEN_NUMBER], ebp_offset[LEN_NUMBER], *row[STACK_COLUMN_TITLES];
	uint stack_size, shift, length, tos;
	uint32 stack_bottom, stack_base, stack_value, ptr;
	int ptr_sign;

	if(!cpu.ss.valid || bochs_offline || bochs_running) return;

	if((cpu.cr0 & 1) && (cpu.ss.descriptor[1] & 0x400000)){		// check for a 32bit stack
		stack_size = 4;
		shift = 2;
		if(!virtual_to_physical(&cpu.ss, 0, &stack_base)){
			g_print("BFE: failed to calculate stack base, stack not updated.\n");
			return;
		}
		gtk_label_set_text(GTK_LABEL(stack_label), "32bit Stack");
	} else {
		stack_size = 2;
		shift = 1;
		stack_base = cpu.ss.segment<<4;
		gtk_label_set_text(GTK_LABEL(stack_label), "16bit Stack");
	}
	stack_bottom = (stack_below << shift) >= cpu.esp ? 0 : cpu.esp - (stack_below << shift);
	
	row[0] = esp_offset;
	row[1] = value;
	row[2] = ebp_offset;
	length = 0;
	tos = -1;

	gtk_clist_freeze(stack_list);
	gtk_clist_clear(stack_list);

	ptr = cpu.esp + (stack_above << shift);
	ptr_sign = ptr < (uint)4 ? 1 : -1;
	for(; ptr >= stack_bottom; ptr -= stack_size){
		if(ptr_sign > 0 && (int)ptr < 0) break;
		if(stack_size == 2){
			fprintf(writepipe, "x /1hx 0x%X\n", stack_base + ptr);
		} else {
			fprintf(writepipe, "x /1wx 0x%X\n", stack_base + ptr);
		}
		if(fscanf(readpipe, "%*s %*s 0x%x", &stack_value) == 1){
			if(ptr == cpu.esp){
				snprintf(esp_offset, LEN_NUMBER, "TOS");
				tos = length;
			} else {
				snprintf(esp_offset, LEN_NUMBER, "%+d", (int)(ptr - cpu.esp));
			}
			snprintf(ebp_offset, LEN_NUMBER, "%+d", (int)(ptr - cpu.ebp));
			if(stack_size == 2) snprintf(value, LEN_NUMBER, "%.4X", stack_value);
			else snprintf(value, LEN_NUMBER, "%.8X", stack_value);

			if(((stack_size == 2) && !(ptr >= 0x10000)) || (stack_size == 4)){
				gtk_clist_append(stack_list, row);
				length++;
			}
		}
		prompt_read();
		if(ptr == 0) break;
	}
	gtk_clist_thaw(stack_list);
	if(tos != -1){
		gtk_clist_moveto(stack_list, tos, 0, 0.5, 0.0);
		gtk_clist_select_row(stack_list, tos, 0);
	}
}
