/*
 * Remote debugging interface for vxworks shell debugger for GDB, the GNU
 * debugger. Copyright 1995 Free Software Foundation, Inc.
 * 
 * Written by Stu Grossman of Cygnus Support
 * 
 * This file is part of GDB.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "defs.h"
#include "gdbcore.h"
#include "target.h"
#include "monitor.h"
#include "serial.h"

#include "inferior.h"
#include "breakpoint.h"
#include <assert.h>

extern struct breakpoint *breakpoint_chain;
extern int      remote_debug;
extern void     monitor_command();
extern serial_t monitor_desc;
extern int      remove_breakpoints();
extern int      vxshell_hit_breakpoint;

#define monitor_printf monitor_printf_noecho

static void vxshell_open PARAMS((char *args, int from_tty));

struct bd_list_info {
	struct bd_list_info *next;
	int             addr;
};

static struct bd_list_info *bd_list = 0;
static struct bd_list_info *bd_list_end = 0;

static char     hjb_buf[10000];	/* XXX */
char            hjb_buf2[10000];/* XXX */
void            (*fputs_unfiltered_hook) ();


/*
 * This array of registers needs to match the indexes used by GDB. The whole
 * reason this exists is because the various ROM monitors use different names
 * than GDB does, and don't support all the registers either.
 */

static char    *vxshell_regnames[NUM_REGS] =
{
	"0", "1", "2", "3", "4", "5", "6", "7",	/* d0-7 */
	"8", "9", "10", "11", "12", "13", "14", "15",	/* a0-7 */
	"16", "17",		/* sr, pc */
};

static void
vxshell_supply_register(regname, regnamelen, val, vallen)
	char           *regname;
	int             regnamelen;
	char           *val;
	int             vallen;
{
	int             regno;

	if (regnamelen != 2)
		return;

	regno = atoi(regname);
	monitor_supply_register(regno, val);
}

/*
 * Define the monitor command strings. Since these are passed directly
 * through to a printf style function, we need can include formatting
 * strings. We also need a CR or LF on the end.
 */

static struct target_ops vxshell_ops;

static char    *vxshell_inits[] = {"gdb-synch\r\n", NULL};

static struct monitor_ops vxshell_cmds =
{
	MO_CLR_BREAK_USES_ADDR,
	vxshell_inits,		/* Init strings */
	"cont\r\n",		/* continue command */
	"step\r\n",		/* single step */
	NULL,			/* interrupt command */
	"brk %x\r\n",		/* set a breakpoint */
	"dummy %x\r\n",		/* clear a breakpoint */
	"dball\r\n",		/* clear all breakpoints */
	NULL,
	{
		"mset_b %x %02x\r\n",	/* setmem.cmdb (addr, value) */
		"mset_w %x %04x\r\n",	/* setmem.cmdw (addr, value) */
		"mset_l %x %08x\r\n",	/* setmem.cmdl (addr, value) */
		NULL,		/* setmem.cmdll (addr, value) */
		NULL,		/* setreg.resp_delim */
		NULL,		/* setreg.term */
		NULL,		/* setreg.term_cmd */
	},
	{
		"mdump_b %x %x\r\n",	/* getmem.cmdb (addr, len) */
		"mdump_w %x %x\r\n",	/* getmem.cmdw (addr, len) */
		"mdump_l %x %x\r\n",	/* getmem.cmdl (addr, len) */
		NULL,		/* getmem.cmdll (addr, len) */
		" ",		/* getmem.resp_delim */
		"end-of-getmem",		/* getmem.term */
		NULL,		/* getmem.term_cmd */
	},
	{
		"rset %s %x\r\n",	/* setreg.cmd (name, value) */
		NULL,		/* setreg.resp_delim */
		NULL,		/* setreg.term */
		NULL		/* setreg.term_cmd */
	},
	{
		"rget %s\r\n",	/* getreg.cmd (name) */
		"=",		/* getreg.resp_delim */
		NULL,		/* getreg.term */
		NULL		/* getreg.term_cmd */
	},
	"rdump\r\n",		/* dump_registers */
	"\\(\\w+\\) +=\\([0-9a-fA-F]+\\b\\)",	/* register_pattern */
	vxshell_supply_register,/* supply_register */
	NULL,			/* load_routine (defaults to SRECs) */
	NULL,			/* download command */
	NULL,			/* load response */
	"gdb-synch",		/* monitor command prompt */
	"\r\n",			/* end-of-line terminator */
	NULL,			/* optional command terminator */
	&vxshell_ops,		/* target operations */
	SERIAL_1_STOPBITS,	/* number of stop bits */
	vxshell_regnames,	/* registers names */
	MONITOR_OPS_MAGIC	/* magic */
};

static void
vxshell_open(args, from_tty)
	char           *args;
	int             from_tty;
{
	monitor_open(args, &vxshell_cmds, from_tty);
}

static void
toggle_remote_debug(char *args, int from_tty)
{
	if (remote_debug) {
		fputs_unfiltered("remote_debug disabled\n", gdb_stderr);
		remote_debug = 0;
	} else {
		fputs_unfiltered("remote_debug enabled\n", gdb_stderr);
		remote_debug = 1;
	}
}

void
curtid_command(args, from_tty)
	char           *args;
	int             from_tty;
{
	char           *p;
	int             resp_len;
	int             tid;

	if (monitor_desc == NULL)
		error("monitor target not open.");

	if (args == 0)
		error("task-id argument required\n");

	p = PROMPT;

	/*
	 * Send the command.  Note that if no args were supplied, then we're
	 * just sending the monitor a newline, which is sometimes useful.
	 */

	monitor_printf("set_tid %s\r\n", (args ? args : ""));

	resp_len = monitor_expect_prompt(hjb_buf, sizeof hjb_buf);

	sscanf(args, "0x%x", &tid);

	if (tid != 0 && tid != inferior_pid) {
		inferior_pid = tid;
		flush_cached_frames();
		registers_changed();
		stop_pc = read_pc();
		select_frame(get_current_frame(), 0);
	}
}

void
list_tasks_command(args, from_tty)
	char           *args;
	int             from_tty;
{
	char           *p;
	int             resp_len;
	int             tid;

	if (monitor_desc == NULL)
		error("monitor target not open.");

	p = PROMPT;

	/*
	 * Send the command.  Note that if no args were supplied, then we're
	 * just sending the monitor a newline, which is sometimes useful.
	 */

	monitor_printf("! i \r\n");

	resp_len = monitor_expect_prompt(hjb_buf, sizeof hjb_buf);

	fputs_unfiltered(hjb_buf, gdb_stderr);	/* Output the response */
}

void
which_task_command(args, from_tty)
	char           *args;
	int             from_tty;
{
	char           *p;
	int             resp_len;
	int             tid;

	if (monitor_desc == NULL)
		error("monitor target not open.");

	p = PROMPT;

	/*
	 * Send the command.  Note that if no args were supplied, then we're
	 * just sending the monitor a newline, which is sometimes useful.
	 */

	monitor_printf("get_tid \r\n");

	resp_len = monitor_expect_prompt(hjb_buf, sizeof hjb_buf);

	fputs_unfiltered(hjb_buf, gdb_stderr);	/* Output the response */
}

void
del_sp_cmd(args, from_tty)
	char           *args;
	int             from_tty;
{
	char           *p;
	int             resp_len;
	int             tid;

	if (monitor_desc == NULL)
		error("monitor target not open.");

	if (args == 0)
		error("argument required\n");

	p = PROMPT;

	/*
	 * Send the command.  Note that if no args were supplied, then we're
	 * just sending the monitor a newline, which is sometimes useful.
	 */

	monitor_printf("del_sp %s \r\n", args);

	resp_len = monitor_expect_prompt(hjb_buf, sizeof hjb_buf);
}

void
vxshell_del_break(int addr)
{
	int             resp_len, i;
	struct bd_list_info *bd, *tmp;
	struct breakpoint *bp;

	bp = (struct breakpoint *)addr;

	if (addr == 0)
		return;

	if (vxshell_hit_breakpoint == 1) {
		bd = (struct bd_list_info *) 
			malloc(sizeof(struct bd_list_info));
		assert(bd != 0);
		bd->addr = bp->address;

		if (bd_list == 0) {
			bd_list = bd_list_end = bd;
			bd_list->next = 0;
		} else {
			bd_list_end->next = bd;
			bd_list_end = bd;
			bd_list_end->next = 0;
		}
		return;
	}

	if (addr == -1) {
		bd = bd_list;

		while (bd) {
			monitor_printf("dbrk-real %x\r\n", bd->addr);
			resp_len = monitor_expect_prompt(hjb_buf, 
					sizeof hjb_buf);
			tmp = bd;
			bd = bd->next;
			free(bd);
		}

		bd_list = 0;
		bd_list_end = 0;
		return;
	}

	monitor_printf("dbrk-real %x\r\n", bp->address);
	resp_len = monitor_expect_prompt(hjb_buf, sizeof hjb_buf);
}

void
vxshell_unfiltered_hook(char *linebuffer, FILE * stream)
{
	char           *from;
	int             i;

	for (i = 0, from = linebuffer; i < sizeof(hjb_buf2) && 
	     *from != '\0'; from++) {
		if (*from != '
') {
			hjb_buf2[i] = *from;
			i++;
		}
	}
	hjb_buf2[i] = '\0';
	fputs(hjb_buf2, stream);
}

void
_initialize_vxshell_rom()
{
	init_monitor_ops(&vxshell_ops);

	vxshell_ops.to_shortname = "vxgdb";
	vxshell_ops.to_longname = "remote gdb interface to vxshell monitor";
	vxshell_ops.to_doc = "debug via the vxworks target resident shell monitor.\n\
specify the serial device it is connected to (e.g. /dev/ttya or hostname:port).";

	vxshell_ops.to_open = vxshell_open;

	add_target(&vxshell_ops);

	fputs_unfiltered_hook = vxshell_unfiltered_hook;

	add_com("toggle_remote_debug", class_vxworks, toggle_remote_debug,
		"Toggle remote_debug flag");

	add_com("vxsh", class_vxworks, monitor_command,
		"Send a command to the vxshell debug monitor.");

	add_com("select_task", class_vxworks, curtid_command,
		"Select a task to be debugged.");

	add_com("which_task", class_vxworks, which_task_command,
		"Show current task id being debugged.");

	add_com("list_tasks", class_vxworks, list_tasks_command,
		"List vxworks tasks.");

	add_com("spawn", class_vxworks, del_sp_cmd,
		"Delay 6 seconds and spawn a vxworks task. (e.g., spawn l 0x80023841)");
}

