/*
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */
#include <lcrash.h>

static uint64_t value;
static int bcount = 0;
static int acount = 0;
char *fname = (char *)NULL; 

void dis_usage(command_t *);
kaddr_t print_instr_stream(kaddr_t, int, int, int, FILE *);

/*
 * dis_cmd() -- Run the 'dis' command.
 */
int
dis_cmd(command_t *cmd)
{
	int size;
	char *c = (char *)NULL;
	syment_t *sp;
	kaddr_t v;
	uint64_t val;

	/* Dump a list of all instructions
	 */
	if (cmd->flags & C_LIST) {
		list_instructions(cmd->ofp);
		return(0);
	}

	/* Disassemble all of the instructions in the kernel (more or
	 * less). Mainly used for testing purposes (to compare with the
	 * output from idis).
	 */
	if (cmd->flags & C_ALL) {
		kaddr_t t_end;

		sp = kl_lkup_symname("_text");
		v = sp->s_addr;
		sp = kl_lkup_symname("_etext");
		t_end = sp->s_addr;
		while (v <= t_end) {
			if (cmd->flags & C_FULL) {
				size = print_instr(v, cmd->ofp, 1);
			} else {
				size = print_instr(v, cmd->ofp, 0);
			}
			v += size;
		}
		return(0);
	}

	/* Disassemble all instructions for a given function
	 */
	if (fname) {
		int fsize;
		kaddr_t faddr;

		if (!(sp = kl_lkup_symname(fname))) {
			fname = (char*)NULL;
			return(1);
		}
		fsize = kl_funcsize(sp->s_addr);
		faddr = sp->s_addr;
		while (faddr < (sp->s_addr + fsize)) {
			if (cmd->flags & C_FULL) {
				size = print_instr(faddr, cmd->ofp, 1);
			} else {
				size = print_instr(faddr, cmd->ofp, 0);
			}
			faddr += size;
		}
		fname = (char*)NULL;
		return(0);
	}

	/* Make sure we have the right number of parameters
	 */
	if (cmd->nargs > 3) {
		dis_usage(cmd);
		return (1);
	}

	/* Set the disassembler's location and the number of lines to
	 * dump out before and after pc.  Also make sure our line 
	 * count is correct (if only one count parameter is specified).
	 */
	if (cmd->nargs) {
		if ((c = strpbrk(cmd->args[0], "+-/*"))) {
			GET_VALUE(cmd->args[0], &value);
			if (KL_ERROR) {
				kl_print_error();
				return(1);
			}
		} else {
			if ((sp = kl_lkup_symname(cmd->args[0]))) {
				value = (uint64_t)sp->s_addr;
			} else {
				GET_VALUE(cmd->args[0], &value);
				if (KL_ERROR) {
					kl_print_error();
					return(1);
				}
			}
		}
		if (cmd->nargs == 1) {
			bcount = acount = 0;
		} else if (cmd->nargs == 2) {
			GET_VALUE(cmd->args[1], &val);
			if (KL_ERROR) {
				kl_print_error();
				return(1);
			}
			acount = (int)val;
			if (acount < 0) {
				KL_ERROR = KLE_BAD_LINENO;
				kl_print_error();
				dis_usage(cmd);
				return(1);
			}
			/* Treat acount as a line count (have to subtract 
			 * the current instruction).
			 */
			if (acount) {
				acount--;
			}
			bcount = 0;
		} else if (cmd->nargs == 3) {
			GET_VALUE(cmd->args[1], &val);
			if (KL_ERROR) {
				kl_print_error();
				return(1);
			}
			bcount = (int)val;
			if (bcount < 0) {
				KL_ERROR = KLE_BAD_LINENO;
				kl_print_error();
				dis_usage(cmd);
				return(1);
			}
			GET_VALUE(cmd->args[2], &val);
			if (KL_ERROR) {
				kl_print_error();
				return(1);
			}
			acount = (int)val;
			if (acount < 0) {
				KL_ERROR = KLE_BAD_LINENO;
				kl_print_error();
				dis_usage(cmd);
				return(1);
			}
		} 
	} else {
		/* Make sure that there is a value...
		 */
		if (!value) {
			dis_usage(cmd);
			return(1);
		}
	}
	value = (kaddr_t)print_instr_stream(value, bcount, acount, 
		cmd->flags, cmd->ofp);
	return(0);
}

#define _DIS_USAGE "[-f] [-w outfile] [-F funcname]|addr[count|[bcount acount]]"

/*
 * dis_usage() -- Print the usage string for the 'dis' command.
 */
void
dis_usage(command_t *cmd)
{
	CMD_USAGE(cmd, _DIS_USAGE);
}

/*
 * dis_help() -- Print the help information for the 'dis' command.
 */
void
dis_help(command_t *cmd)
{
	CMD_HELP(cmd, _DIS_USAGE,
		"Display the disassembled code for addr for count "
		"instructions (the default count is 1). Alternately, "
		"display the disassembled code for addr with bcount "
		"instructions before and acount instructions after. "
		"If bcount or acount is zero, then no instructions will "
		"be displayed before or after respectively. If the dis "
		"command is issued with the -f command line option, "
		"additional information will be displayed (opcode and "
		"byte size). If the dis command is issued with the -F "
		"option followed by funcname, disassembled code "
		"will be displayed for all instructions in the function.");
}


/*
 * dis_parse() -- Parse the command line arguments for the 'dis' command.
 */
int
dis_parse(command_t *cmd)
{
	option_t *op;

	if (set_cmd_flags(cmd, (C_WRITE|C_ALL|C_FULL), "F:")) {
		return(1);
	}
	op = cmd->options;
	while (op) {
		switch(op->op_char) {
			case 'F':
				fname = op->op_arg;
				break;
		}
		op = op->op_next;
	}
	return (0);
}

/*
 * dis_complete() -- Complete arguments of 'dis' command.
 */
char *
dis_complete(command_t *cmd)
{
	char *ret;

	/* complete standard options (for example, -w option) arguments
	 */
	if ((ret = complete_standard_options(cmd)) != NOT_COMPLETED) {
		return(ret);
	}
	fprintf(cmd->ofp, "\n");
	dis_usage(cmd);
	return(DRAW_NEW_ENTIRE_LINE);
}
