/*
 * Copyright (c) 2003-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagcam256.c 11428 2009-06-23 06:55:26Z vladimir $
 */

/* DAG headers. */
#include "dagapi.h"
#include "dagreg.h"
#include "dag_platform.h"
#include "dagutil.h"
#include "dagclarg.h"

#if HAVE_EDITLINE
#include <editline/readline.h>
#include <histedit.h>
#endif /* HAVE_EDITLINE */


/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id: dagcam256.c 11428 2009-06-23 06:55:26Z vladimir $";
static const char* const kRevisionString = "$Revision: 11428 $";

#define MAXARGV 64

/* Required for dagcam to work. */
#define USE_NANOSLEEPS 1


static char *dgetline(char *prompt);

#define DEFAULT_DAGNAME "dag0"
#define DEFAULT_DAGCAM256_HISTORY ".dagcam256_history"
#define DEFAULT_DAGCAM256_RCFILE ".dagcam256rc"

static char dagname_buf[DAGNAME_BUFSIZE] = DEFAULT_DAGNAME;
static char dagname[DAGNAME_BUFSIZE];
static int dagstream = 0;
static int dagfd;
static uint8_t *dagiom;
static volatile uint32_t *csr __attribute__((unused));
static volatile uint32_t *srch __attribute__((unused));
static volatile uint32_t *copro_regs;

#define COMMAND_BUFSIZE 128
#define FILENAME_BUFSIZE 128
static char command_buf[COMMAND_BUFSIZE] = "";
static char history_filename_buf[FILENAME_BUFSIZE] = DEFAULT_DAGCAM256_HISTORY;
static char rc_filename_buf[FILENAME_BUFSIZE] = DEFAULT_DAGCAM256_RCFILE;

static int uHistoryCount;
static int uInteractive = 1; /* the default is to go interactive */
static int uHaveReadCommandLine = 0;
static int uReadFromKeyboard = 1;
static int uSaveHistory = 1;

static uint32_t prototype  __attribute__((unused)) = 0;
static uint32_t wide_bus  __attribute__((unused)) = 0;
static uint32_t cam_busy  __attribute__((unused)) = 1;
static char white[] = " \t\n";


#define SC256_CSR0 (0x54/4)
#define SC256_CSR1 (0x58/4)
#define SC256_CSR2 (0x5c/4)
#define SC256_VAL_ARRAY 0x00
#define SC256_RES_ARRAY (0x48/4)
#define SC256_VAL_ARRAY_DEPTH (0x12/4)
enum {
	SCR	= 0x00,
	IDR	= 0x01,
	SSR0	= 0x02,
	nor3	= 0x03, // Reserved
	SSR1	= 0x04,
	nor5	= 0x05, // Reserved
	LAR	= 0x06,
	nor7	= 0x07, // Reserved
	RSLT0	= 0x08,
	RSLT1	= 0x09,
	RSLT2	= 0x0a,
	RSLT3	= 0x0b,
	RSLT4	= 0x0c,
	RSLT5	= 0x0d,
	RSLT6	= 0x0e,
	RSLT7	= 0x0f,
	CMPR00	= 0x10,
	CMPR01	= 0x11,
	CMPR02	= 0x12,
	CMPR03	= 0x13,
	CMPR04	= 0x14,
	CMPR05	= 0x15,
	CMPR06	= 0x16,
	CMPR07	= 0x17,
	CMPR08	= 0x18,
	CMPR09	= 0x19,
	CMPR10	= 0x1a,
	CMPR11	= 0x1b,
	CMPR12	= 0x1c,
	CMPR13	= 0x1d,
	CMPR14	= 0x1e,
	CMPR15	= 0x1f,
	GMR00	= 0x20,
	GMR01	= 0x21,
	GMR02	= 0x22,
	GMR03	= 0x23,
	GMR04	= 0x24,
	GMR05	= 0x25,
	GMR06	= 0x26,
	GMR07	= 0x27,
	GMR08	= 0x28,
	GMR09	= 0x29,
	GMR10	= 0x2a,
	GMR11	= 0x2b,
	GMR12	= 0x2c,
	GMR13	= 0x2d,
	GMR14	= 0x2e,
	GMR15	= 0x2f,
	GMR16	= 0x30,
	GMR17	= 0x31,
	GMR18	= 0x32,
	GMR19	= 0x33,
	GMR20	= 0x34,
	GMR21	= 0x35,
	GMR22	= 0x36,
	GMR23	= 0x37,
	GMR24	= 0x38,
	GMR25	= 0x39,
	GMR26	= 0x3a,
	GMR27	= 0x3b,
	GMR28	= 0x3c,
	GMR29	= 0x3d,
	GMR30	= 0x3e,
	GMR31	= 0x3f,
	MAX_REGN // Must be last entry in table
};

static const char *regname[] ={ // must be UPPERCASE
	"SCR"   , "IDR"   , "SSR0"  , "NOREG3", //  0  3
	"SSR1"  , "NOREG5", "LAR"   , "NOREG7", //  4  7
	"RSLT0" , "RSLT1" , "RSLT2" , "RSLT3" , //  8 11
	"RSLT4" , "RSLT5" , "RSLT6" , "RSLT7" , // 12 15
	"CMPR00", "CMPR01", "CMPR02", "CMPR03", // 16 19
	"CMPR04", "CMPR05", "CMPR06", "CMPR07", // 20 23
	"CMPR08", "CMPR09", "CMPR10", "CMPR11", // 24 27
	"CMPR12", "CMPR13", "CMPR14", "CMPR15", // 28 31
	"GMR00" , "GMR01" , "GMR02" , "GMR03" , // 32 35
	"GMR04" , "GMR05" , "GMR06" , "GMR07" , // 36 39
	"GMR08" , "GMR09" , "GMR10" , "GMR11" , // 40 43
	"GMR12" , "GMR13" , "GMR14" , "GMR15" , // 44 47
	"GMR16" , "GMR17" , "GMR18" , "GMR19" , // 48 51
	"GMR20" , "GMR21" , "GMR22" , "GMR23" , // 52 55
	"GMR24" , "GMR25" , "GMR26" , "GMR27" , // 56 59
	"GMR28" , "GMR29" , "GMR30" , "GMR31"   // 60 63
};



typedef struct cmd
{
	char *name;
	void (*func) (int argc, char *argv[]);

} cmd_t;

typedef struct bit72_word_t
{
	uint32_t data0; // lower order 32-bits
	uint32_t data1;
	uint8_t data2; // higher order 8-bits
	uint8_t valid; // non zero if dataX contains something
	
} bit72_word_t;

typedef struct bit576_word_t
{
	uint32_t data[18]; // lower order 32-bits
	uint8_t bitcnt;    // valid bits, 0, 72, 144, 288, 576
	uint8_t wordcnt;   // valid 32-bit words, including partial words
					   // 0, 3, 6, 9, 18
	
} bit576_word_t;

static void dag_read_history(const char* filename);
static void dag_write_history(const char* filename);

static uint8_t findn_reg( char *ptr );
static bit72_word_t parse72( int argc, char *argv[], int offset, int *more );
static void wval( bit72_word_t ); // copies struct. inefficient
static bit72_word_t rval( void ); // copies struct. inefficient

static void write_reg(uint8_t addr, uint32_t reg_val);
static uint32_t read_reg(uint8_t addr);
static bit72_word_t read_cam_reg(uint8_t addr);
static void write_cam_reg(uint8_t addr, bit72_word_t reg_val);
static void cmd_help(int argc, char *argv[]);
static void cmd_quit(int argc, char *argv[]);
static void cmd_verbose(int argc, char *argv[]);
static void cmd_csr(int argc, char *argv[]);
static void cmd_wcsr0(int argc, char *argv[]);
static void cmd_wcsr1(int argc, char *argv[]);
static void cmd_wcsr2(int argc, char *argv[]);
static void cmd_val_array(int argc, char *argv[]);
static void cmd_result_reg(int argc, char *argv[]);
static void cmd_rcam_reg(int argc, char *argv[]);
static void cmd_wcam_reg(int argc, char *argv[]);
static void cmd_reset(int argc, char *argv[]);
static void cmd_srchen(int argc, char *argv[]);
static void cmd_inhibit(int argc, char *argv[]);
static void cmd_search(int argc, char *argv[]);
static void cmd_learn(int argc, char *argv[]);
static void cmd_wbrst(int argc, char *argv[]);
static void cmd_cam_space(int argc, char *argv[]);
static void cmd_init(int argc, char *argv[]);
static void cmd_dbg(int argc, char *argv[]);


static cmd_t cmdtab[] =
{
	/* Software */
	{"help", cmd_help},
	{"?", cmd_help},
	{"quit", cmd_quit},
	{"verbose", cmd_verbose},
	
	/* Firmware */
	{"csr", cmd_csr},
	{"rcsr", cmd_csr},
	{"wcsr0", cmd_wcsr0},
	{"wcsr1", cmd_wcsr1},
	{"wcsr2", cmd_wcsr2},
	{"rval", cmd_val_array},
	{"wval", cmd_val_array},
	{"result", cmd_result_reg},
	{"reset", cmd_reset},
	{"srchen", cmd_srchen},
	{"inhibit", cmd_inhibit},
	{"dbg", cmd_dbg},
	
	/* Registers */
	{"f", cmd_rcam_reg},
	{"t", cmd_wcam_reg},
	{"rreg", cmd_rcam_reg},
	{"wreg", cmd_wcam_reg},
	{"rscr", cmd_rcam_reg},
	{"wscr", cmd_wcam_reg},
	{"ridr", cmd_rcam_reg},
	{"rssr0", cmd_rcam_reg},
	{"wssr0", cmd_wcam_reg},
	{"rssr1", cmd_rcam_reg},
	{"wssr1", cmd_wcam_reg},
	{"rlar", cmd_rcam_reg},
	{"wlar", cmd_wcam_reg},
	{"rrslt0", cmd_rcam_reg},
	{"rrslt1", cmd_rcam_reg},
	{"rrslt2", cmd_rcam_reg},
	{"rrslt3", cmd_rcam_reg},
	{"rrslt4", cmd_rcam_reg},
	{"rrslt5", cmd_rcam_reg},
	{"rrslt6", cmd_rcam_reg},
	{"rrslt7", cmd_rcam_reg},
	{"rcmpr00", cmd_rcam_reg},
	{"rcmpr01", cmd_rcam_reg},
	{"rcmpr02", cmd_rcam_reg},
	{"rcmpr03", cmd_rcam_reg},
	{"rcmpr04", cmd_rcam_reg},
	{"rcmpr05", cmd_rcam_reg},
	{"rcmpr06", cmd_rcam_reg},
	{"wcmpr06", cmd_wcam_reg},
	{"rcmpr07", cmd_rcam_reg},
	{"rcmpr08", cmd_rcam_reg},
	{"rcmpr09", cmd_rcam_reg},
	{"rcmpr10", cmd_rcam_reg},
	{"rcmpr11", cmd_rcam_reg},
	{"rcmpr12", cmd_rcam_reg},
	{"rcmpr13", cmd_rcam_reg},
	{"rcmpr14", cmd_rcam_reg},
	{"rcmpr15", cmd_rcam_reg},
	{"rgmr00", cmd_rcam_reg},
	{"wgmr00", cmd_wcam_reg},
	{"rgmr01", cmd_rcam_reg},
	{"wgmr01", cmd_wcam_reg},
	{"rgmr02", cmd_rcam_reg},
	{"wgmr02", cmd_wcam_reg},
	{"rgmr03", cmd_rcam_reg},
	{"wgmr03", cmd_wcam_reg},
	{"rgmr04", cmd_rcam_reg},
	{"wgmr04", cmd_wcam_reg},
	{"rgmr05", cmd_rcam_reg},
	{"wgmr05", cmd_wcam_reg},
	{"rgmr06", cmd_rcam_reg},
	{"wgmr06", cmd_wcam_reg},
	{"rgmr07", cmd_rcam_reg},
	{"wgmr07", cmd_wcam_reg},
	{"rgmr08", cmd_rcam_reg},
	{"wgmr08", cmd_wcam_reg},
	{"rgmr09", cmd_rcam_reg},
	{"wgmr09", cmd_wcam_reg},
	{"rgmr10", cmd_rcam_reg},
	{"wgmr10", cmd_wcam_reg},
	{"rgmr11", cmd_rcam_reg},
	{"wgmr11", cmd_wcam_reg},
	{"rgmr12", cmd_rcam_reg},
	{"wgmr12", cmd_wcam_reg},
	{"rgmr13", cmd_rcam_reg},
	{"wgmr13", cmd_wcam_reg},
	{"rgmr14", cmd_rcam_reg},
	{"wgmr14", cmd_wcam_reg},
	{"rgmr15", cmd_rcam_reg},
	{"wgmr15", cmd_wcam_reg},
	{"rgmr16", cmd_rcam_reg},
	{"wgmr16", cmd_wcam_reg},
	{"rgmr17", cmd_rcam_reg},
	{"wgmr17", cmd_wcam_reg},
	{"rgmr18", cmd_rcam_reg},
	{"wgmr18", cmd_wcam_reg},
	{"rgmr19", cmd_rcam_reg},
	{"wgmr19", cmd_wcam_reg},
	{"rgmr20", cmd_rcam_reg},
	{"wgmr20", cmd_wcam_reg},
	{"rgmr21", cmd_rcam_reg},
	{"wgmr21", cmd_wcam_reg},
	{"rgmr22", cmd_rcam_reg},
	{"wgmr22", cmd_wcam_reg},
	{"rgmr23", cmd_rcam_reg},
	{"wgmr23", cmd_wcam_reg},
	{"rgmr24", cmd_rcam_reg},
	{"wgmr24", cmd_wcam_reg},
	{"rgmr25", cmd_rcam_reg},
	{"wgmr25", cmd_wcam_reg},
	{"rgmr26", cmd_rcam_reg},
	{"wgmr26", cmd_wcam_reg},
	{"rgmr27", cmd_rcam_reg},
	{"wgmr27", cmd_wcam_reg},
	{"rgmr28", cmd_rcam_reg},
	{"wgmr28", cmd_wcam_reg},
	{"rgmr29", cmd_rcam_reg},
	{"wgmr29", cmd_wcam_reg},
	{"rgmr30", cmd_rcam_reg},
	{"wgmr30", cmd_wcam_reg},
	{"rgmr31", cmd_rcam_reg},
	{"wgmr31", cmd_wcam_reg},
	
	/* Spaces */
	{"rcamspace", cmd_cam_space},
	{"wcamspace", cmd_cam_space},
	{"rentry", cmd_cam_space},
	{"wentry", cmd_cam_space},
	{"rmask", cmd_cam_space},
	{"wmask", cmd_cam_space},
	
	/* Abstractions */
	{"search", cmd_search},
	{"learn", cmd_learn},
	{"wbrst", cmd_wbrst},
	{"init", cmd_init},
	{NULL, 0,}
};

static void dagcam256(void);


enum
{
	CLA_HELP,
	CLA_VERSION,
	CLA_VERBOSE,
	CLA_DEVICE,
	CLA_INTERACTIVE,
	CLA_HISTFILE,
	CLA_NOHIST,
	CLA_RCFILE,
	CLA_CMDLINE
};

static void
print_version(void)
{
	printf("dagcam256 (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}
/*****************************************************************************/

static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagcam256 - Endace DAG Coprocessor IDT-CAM shell.\n");
	printf("Usage: dagcam256 [-hvin] [-d device] [-t histfile] [-r rcfile] [-c cmdline] [file]\n"
);
	dagclarg_display_usage(clarg, stdout);
}

static void
write_reg(uint8_t addr, uint32_t reg_val)
{
	*(copro_regs + addr) = reg_val;

#if USE_NANOSLEEPS
	dagutil_nanosleep(100);
#endif

	if (dagutil_get_verbosity())
		printf("Content written to the register at offset 0x%x: %08x\n",addr*4,reg_val);
}


static uint32_t
read_reg(uint8_t addr)
{
	if (dagutil_get_verbosity())
		printf("\nRegister at offset 0x%08x has been read\n",addr*4);
		
	return *(copro_regs + addr);
}


static bit72_word_t
read_cam_reg(uint8_t addr)
{
	bit72_word_t reg_val;
	uint8_t inst = 0, acc = 3;
	int timeout = 1023;
	
	*(copro_regs + SC256_CSR0) = 0;
	*(copro_regs + SC256_CSR1) = (((inst & 0x7) << 28 ) | ((acc & 0x3) << 20) | (addr & 0xfffff)); 
	*(copro_regs + SC256_CSR2) = 0x1; // Set 'action' bit

	while ((timeout > 0) && ( *(copro_regs + SC256_CSR2) & 0x000c0000)== 0x0 )
	{
#if USE_NANOSLEEPS
		dagutil_nanosleep(110);
#else
#error nanosleeps required for read_cam_reg to work
#endif /* USE NANOSLEEPS */

		timeout--;
	}

	if (dagutil_get_verbosity())
	{
		printf("Contents written to the CSR0: 0 CSR1: %08x CSR2: 0x00000001\n",(((inst & 0x7) << 28 ) | ((acc & 0x3) << 20) | (addr & 0xfffff)));
	}

	reg_val.data0 = *(copro_regs + SC256_RES_ARRAY);
	reg_val.data1 = *(copro_regs + SC256_RES_ARRAY + 1);
	reg_val.data2 = (*(copro_regs + SC256_RES_ARRAY + 2) & 0xff);
	reg_val.valid = 1;
	
	return reg_val;
}


static void
write_cam_reg(uint8_t addr, bit72_word_t reg_val)
{
	uint8_t inst = 1, acc = 3;

	*(copro_regs + SC256_VAL_ARRAY) = reg_val.data0;
	*(copro_regs + SC256_VAL_ARRAY + 1) = reg_val.data1;
	*(copro_regs + SC256_VAL_ARRAY + 2) = reg_val.data2;

	*(copro_regs + SC256_CSR0) = 0;
	*(copro_regs + SC256_CSR1) = (((inst & 0x7) << 28 ) | ((acc & 0x3) << 20) | (addr & 0xfffff)); 
	*(copro_regs + SC256_CSR2) = 0x1; // Set 'action' bit
	
#if USE_NANOSLEEPS
	dagutil_nanosleep(180); /* can be dropped down to 90ns on RevB cards */
#endif /* USE_NANOSLEEPS */

	if (dagutil_get_verbosity())
	{
		printf("Contents written to the value array: %02x %08x %08x\n",reg_val.data2,reg_val.data1,reg_val.data0);
		printf("Contents written to CSR0 0x00000000 CSR1 %08x CSR2 0x1\n",(((inst & 0x7) << 28 ) | ((acc & 0x3) << 20) | (addr & 0xfffff)));
	}
}


int
dagcam256_main(int argc, char *argv[])
{
	dag_reg_t copro_register_base_info[DAG_REG_MAX_ENTRIES];
	ClArgPtr clarg;
	FILE *errorfile = NULL;
	int argindex;
	int code;
	int clarg_result;

	dagutil_set_progname("dagcam256");
	fflush(stdout);

	/* Set up default DAG device. */
	if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
	{
		dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
	}

	clarg = dagclarg_init(argc, (const char* const *) argv);

	dagclarg_add(clarg, "display help (this page).", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');
	dagclarg_add(clarg, "display version information.", "--version", 'V', CLA_VERSION);
	dagclarg_add(clarg, "increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add_string(clarg, "DAG device to use. default: dag0.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "interactive, even when reading from a file or command line", "--interactive", 'i', CLA_INTERACTIVE);
	dagclarg_add_string(clarg, "read history file (default is .dagcam_history)", "--history", 't', "histfile", history_filename_buf, FILENAME_BUFSIZE, CLA_HISTFILE);
	dagclarg_add(clarg, "no history (turn off reading and writing .dagcam_history)", "--nohistory", 'n', CLA_NOHIST);
	dagclarg_add_string(clarg, "read initial commands from rcfile (default is .rc_filename_buf)", "--rcfile", 'r', "rcfile", rc_filename_buf, FILENAME_BUFSIZE, CLA_RCFILE);
	dagclarg_add_string(clarg, "execute cmdline (instead of stdin processing)", "--cmdline", 'c', "cmdline", command_buf, COMMAND_BUFSIZE, CLA_CMDLINE);
	

	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;

			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				errorfile = stderr;
				break;

			case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;

			case CLA_DEVICE:
				if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
				{
					dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
				}
				break;

			case CLA_INTERACTIVE:
				uInteractive++;
				break;

			case CLA_NOHIST:
				uSaveHistory = 0;
				break;

			case CLA_CMDLINE:
			case CLA_HISTFILE:
			case CLA_RCFILE:
				break;

			default:
			/* Unknown option. */
				dagutil_error("unknown option %s\n", argv[argindex]); 
				print_usage(clarg);
				return EXIT_FAILURE;
		}
		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}


	if (-1 == clarg_result)
	{
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]);

		}
		dagclarg_display_usage(clarg, stderr);
		return EXIT_FAILURE;
	}



	argv = (char **)dagclarg_get_unprocessed_args(clarg, &argc);
	
	
	/*
	 * This is the case when a command line argument is taken
	 * as the input when dagcam running as shell interpreter.
	 */
	if (argc)
	{
		if (freopen(argv[0], "r", stdin) != stdin)
			dagutil_panic("cannot open %s: %s\n", argv[0], strerror(errno));
		argc--;
		argv++;
	}

	if (argc)
		dagutil_warning("excessive command line arguments: '%s'%s\n", argv[0], (argc > 1) ? " and further" : "");

	/* A single command line command to execute. */
	if (strlen(command_buf) > 0)
	{
		uReadFromKeyboard = 1;
		uInteractive = 0;
		uSaveHistory = 0;
	}

	/*
	 * Multiple commands, either file or interactive.
	 */
	if (0 == isatty(fileno(stdin)))
		uInteractive = 0;

#if HAVE_EDITLINE
	using_history();
#endif /* HAVE_EDITLINE */

	dagfd = dag_open(dagname);
	if (dagfd < 0) 
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));

	dagiom = dag_iom(dagfd);
	if (dagiom == NULL)	
	{
		fprintf(stderr, "dagcam256 main(): dag_iom() failed: %s\n", strerror(errno));
		return 0;
	}

	/* Find the CAM I/O locations. */
	if (dag_reg_find((char*) dagiom, DAG_REG_IDT_TCAM, copro_register_base_info) < 0)
	{
		fprintf(stderr, "dagipf_init(): failed to find coprocessor: %s\n", strerror(errno));
		return EXIT_FAILURE;
	}

	copro_regs = (volatile uint32_t*) (dagiom + copro_register_base_info[0].addr);

	/*
	 * Initialization.
	 */
	if (1 == uInteractive)
	{
		int safed;
		FILE *fp;

		/*
		 * Read in rc file without closing stdin, stdout is untouched.
		 * Since readline is not used here we don't have any problems.
		 */
		safed = dup(STDIN_FILENO);
		if (safed < 0)
			dagutil_panic("cannot duplicate stdin: %s\n", strerror(errno));
		if (freopen(rc_filename_buf, "r", stdin) != stdin)
		{
			/*
			 * Probably doesn't exist
			 */
			(void) fclose(stdin); /* paranoia? */
		}
		else
		{
			dagcam256();
			if (fclose(stdin) < 0)
				dagutil_panic("cannot fclose %s on stdin: %s\n", rc_filename_buf, strerror(errno));
		}
		if (dup(safed) != STDIN_FILENO)
			dagutil_panic("cannot replicate stdin: %s\n", strerror(errno));

		fp = fdopen(STDIN_FILENO, "r");
		if (fp == NULL)
			dagutil_panic("fdopen on old stdin failed: %s\n", strerror(errno));

		*stdin = *fp; /* interesting to see that this works */
		
#if HAVE_EDITLINE
		clear_history();
#endif /* HAVE_EDITLINE */

		uReadFromKeyboard = 1;
	}
	else
	{
		uSaveHistory = 0;
	}

	dag_read_history(history_filename_buf);
	
#if HAVE_EDITLINE
	uHistoryCount = where_history();
#endif /* HAVE_EDITLINE */

	dagcam256();
	
	dag_write_history(history_filename_buf);

	return EXIT_SUCCESS;
}


static void
dag_read_history(const char* filename)
{
#if HAVE_EDITLINE
	if (0 == uSaveHistory)
	{
		return;
	}

	(void) read_history(filename);
#endif /* HAVE_EDITLINE */
}


static void
dag_write_history(const char* filename)
{
#if HAVE_EDITLINE
	if (0 == uSaveHistory)
	{
		return;
	}

	if (write_history(filename))
	{
		dagutil_warning("couldn't save history file %s: %s\n", filename, strerror(errno));
	}
#endif /* HAVE_EDITLINE */
}


static void
dagcam256(void)
{
	char prompt[64];
	char* line;
	char* tok;
	char* remain;
	cmd_t* cmdp;
	char** ap;
	char* argv[MAXARGV];
	char* remain_state;
	char* tok_state;
	int temp ;  

	for (;;)
	{
		(void) sprintf(prompt, "dagcam256:%d> ", uHistoryCount + 1);
		
		line = dgetline(prompt);
		if (line == NULL)
			break;

		/*
		 * It's a bit of a mess here, since we would like to
		 * parse the line only once for spaces and the first
		 * token but we need the line in complete form for
		 * history and shell commands.
		 */
		remain = line + strspn(line, white);

		// Truncate the line at any comment.
		if ((tok = strchr(remain, '#')) != NULL)
			*tok = '\0';
		
		if (*remain == '\0')
		{
			goto done;
		}
		else
		{

#if HAVE_EDITLINE
			add_history(remain);
#endif /* HAVE_EDITLINE */
			uHistoryCount++;

			tok = strtok_r(remain, ";", &remain_state);
			while (tok != NULL)
			{
				char first_char;

				tok += strspn(tok, white);
				first_char = (*tok);

				if ('!' == first_char)
				{
					// Shell command.  
					temp = system(tok+1);
				}
				else if ('\0' == first_char)
				{
					// Do nothing.  
				}
				else
				{
					// Regular command. 
					ap = argv;
					*ap = strtok_r(tok, white, &tok_state);
					while (*ap != NULL)
					{
						if (**ap != '\0')
						{
							if (++ap >= &argv[MAXARGV])
							{
								*--ap = NULL;
								dagutil_warning("too many arguments for command %s\n", argv[0]);
								break;
							}
						}
						*ap = strtok_r(NULL, white, &tok_state);
					}
#if 0
					printf("%s: %d args\n", argv[0], ap-argv);
#endif /* 0 */

					// Find command in command table.  
					for (cmdp = cmdtab; cmdp->name != NULL; cmdp++)
					{
						if (0 == strncmp(argv[0], cmdp->name, strlen(argv[0])))
						{
							(*cmdp->func)(ap-argv, argv);
							break;
						}
					}
					if (cmdp->name == NULL)
						printf("%s: unknown command\n", argv[0]);
				}

				tok = strtok_r(NULL, ";", &remain_state);
			}
		}

	done:
		dagutil_free(line);
	}
}


/* Returns 0 to MAX_REGN-1 for valid; MAX_REGN for invalid
 */
static uint8_t
findn_reg(char *ptr)
{
	uint8_t retval = MAX_REGN;
	int i;
	char *eptr, *nptr;
	char *fptr, *tptr;
	
	if ( NULL == ptr || '\0' == *ptr )
		dagutil_panic("findn_reg: null or zero-length string provided.\n");
	
	retval = strtoul(ptr, &eptr, 16);
	/*
	* if eptr is null, strtoul failed (programmer error?).
	* if eptr is equal to ptr then no number was found (text string?)
	* if eptr doesn't point to '\0', there were non-number characters found
	* (user error, or text string starting with A-F)
	*/
	if ( eptr && eptr != ptr && '\0' == *eptr ) {
		return retval;
	}
	
	/* Not a number? Try our table of handy symbolic names
	*/
	i = strlen(ptr);
	if ( i < 3 || i > 6 )
		return MAX_REGN; // names are all 3-6 chars long
	
	/* grumble grumble case-sensitive strstr. */
	nptr = (char*) dagutil_malloc(i+1);
	if (NULL == nptr)
	{
		dagutil_warning("findnreg: out of memory\n");
		return MAX_REGN;
	}
	
	fptr = ptr;
	tptr = nptr;
	while ( '\0' != *fptr )
	{
		*tptr++ = toupper( *fptr++ ); // regname array all UPPERCASE
	}
	*tptr = '\0';
	
	for (i = 0; i < MAX_REGN; i++ )
	{
		/* find the register name in the argument. Return first match found.
		 * XXXX: this requires "cmpr01" not "cmpr1" or else "cmpr10" false-hits!
		 */
		if ( NULL != strstr( nptr, regname[i] ) )
		{
			dagutil_free( nptr );
			return i;
		}
	}
	
	dagutil_free( nptr );
	
	return MAX_REGN;
}


/*
 * Parse user-provided input looking for a 72-bit number.
 * Parses either a single 18-digit hex number, or three hex numbers
 * (2, 8 and 8 digits respectively)
 * To force single-number-parsing, use a leading _
 *
 * XXXX: We need a help text explaining all that for the user!
 *
 * Note Well: also have to support the case where MORE arguments are provided
 * (for example, if the command line contains 3 72-bit tuples to harvest in
 *  turn)
 */
static bit72_word_t
parse72( int argc, char *argv[], int offset, int *more )
{
	bit72_word_t rv;
	char *str = NULL;
	char *fp, *tp;
	char tmp;
	int fl, tl;

	memset(&rv, 0, sizeof(rv));
	
	/* if offset == 0, argc will be 1 or 3, so argc-offset gives us the
	 * number of arguments we care about
	 */
	if (argc-offset <= 0 ) {
		dagutil_warning( "parse72: illegal number of options presented. No parse performed.\n");
		return rv;
	}
	/* One argument, first argument is long (>32bit), or first argument
	 * has a leading _ and at least one other character
	 */
	if ( strlen( argv[offset] ) > 8 || 1 == argc-offset ||
		(strlen(argv[offset]) > 1 &&  '_' == argv[offset][0]) )
	{
		if ( more )
			*more = 1;
			
		// one string, up to 18 hex characters long
		// step 1, alloc space for string
		fp = argv[offset];
		tl = fl = strlen( fp );
		
		str = (char*) dagutil_malloc( fl+1 );
		if ( NULL == str) {
			dagutil_warning( "parse72 (1): out of memory. No parse performed.\n" );
			return rv;
		}
		tp = str;
		// step 2: copy string, stripping '_' characters
		while ( *fp ) {
			if ( *fp == '_' ) {
				tl--;
				fp++;
				continue;
			}
			*tp++ = *fp++;
		}
		*tp = '\0';
		if ( strlen( str ) > 18 ) {
			dagutil_warning( "parse72 (3): argument \"%s\"->\"%s\" incorrect?\n\
 Expecting (up to) 18 characters representing an 72-bit hex value.\n\
 (with optional _ characters for human read-ability).\n", argv[offset], str );
		}
		fp = tp = str;
		fl = strlen(fp);
		// 22_11111111_00000000, strlen 18
		//    11111111_00000000, strlen 16
		//          11_00000000, strlen 10
		//                 0000, strlen  4
		if ( fl > 8 )
		{
			if ( fl > 16 )
			{
				// all 72 bits
				rv.data0 = strtoul( &(fp[fl-8]), NULL, 16 );
				
				tmp = fp[fl-8];
				fp[fl-8] = '\0';
				rv.data1 = strtoul( &(fp[fl-16]), NULL, 16 );
				fp[fl-8] = tmp;
				
				tmp = fp[fl-16];
				fp[fl-16] = '\0';
				rv.data2 = strtoul( fp, NULL, 16 );
				fp[fl-16] = tmp;
				
				rv.valid = 1;
			}
			else
			{
				// 33-64 bits
				// XXXX: should sanity check return values!
				rv.data0 = strtoul( &(fp[fl-8]), NULL, 16 );
				
				tmp = fp[fl-8];
				fp[fl-8] = '\0';
				rv.data1 = strtoul( fp, NULL, 16 );
				fp[fl-8] = tmp;
				
				rv.data2 = 0;
				rv.valid = 1;
			}
		}
		else
		{
			// less than 32 bits
			// XXXX: should sanity check return values!
			rv.data0 = strtoul( fp, NULL, 16 );
			rv.data1 = 0;
			rv.data2 = 0;
			rv.valid = 1;
		}
		dagutil_free( str );
	}
	else if (3 <= argc-offset )
	{
		// At least three arguments
		if ( more )
			*more = 3;
		if ( strlen( argv[offset]) > 2 )
		{
			dagutil_warning( "parse72 (3): argument \"%s\" incorrect?\n\
 Expecting 2 characters representing an 8-bit hex value.\n", argv[offset] );
		}
		if ( strlen( argv[offset+1]) > 8 )
		{
			dagutil_warning( "parse72 (3): argument \"%s\" incorrect?\n\
 Expecting 8 characters representing an 32-bit hex value.\n", argv[offset+1] );
		}
		if ( strlen( argv[offset+2]) > 8 )
		{
			dagutil_warning( "parse72 (3): argument \"%s\" incorrect?\n\
 Expecting 8 characters representing an 32-bit hex value.\n", argv[offset+2] );
		}
		rv.data2 = strtoul(argv[offset], NULL, 16);
		rv.data1 = strtoul(argv[offset+1], NULL, 16);
		rv.data0 = strtoul(argv[offset+2], NULL, 16);
		rv.valid = 1;
	}
	else
	{
		// not single-argument or three arguments. Bad User.
		dagutil_warning( "parse72: invalid number of arguments %u. No parse performed.\n", argc-offset );
		return rv;
	}
	
	dagutil_verbose( "parse72: %s %02x %08x %08x\n", (rv.valid)?"V":"IV", rv.data2,rv.data1,rv.data0 );
	
	return rv;
}


static void
wval( bit72_word_t v )
{
	if ( !v.valid )
		dagutil_warning( "writing invalid bit72_word_t\n" );
	write_reg(SC256_VAL_ARRAY, v.data0);
	write_reg(SC256_VAL_ARRAY + 1, v.data1);
	write_reg(SC256_VAL_ARRAY + 2, v.data2);
	
}


static bit72_word_t
rval( void )
{
	bit72_word_t v;
	v.data0 = read_reg(SC256_VAL_ARRAY);
	v.data1 = read_reg(SC256_VAL_ARRAY + 1);
	v.data2 = read_reg(SC256_VAL_ARRAY + 2);
	v.valid = 1;
	return v;
}


static void
cmd_help(int argc, char *argv[])
{
	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf("help\n	Lists available commands.\n");
		return;
	}
	/*
	 * The beginning of most of these lines are TAB characters.
	 * Please ensure your editor does not chew on the tabs.
	 */
	/*
	 * Wouldn't it be nice if this data fit on one 80x25 screen?
	 * Maybe we need to put pauses every 20 lines for slow readers?
	 */

	printf("\
Usage:\n\
	help           - this page\n\
	quit           - exit program\n\
	verbose        - increase verbosity\n\
	csr            - reads the Command Status Register 0, 1, 2\n\
	rcsr           - reads the Command Status Register 0, 1, 2\n\
	wcsrN          - writes the Command Status Register N\n\
	                 where N ranges from 0 to 2\n\
	wbrst          - writes the given value to multiple contiguous Data\n\
	                 or Mask space locations\n\
	{rw}val        - reads or writes the 72-bit data in the Value Array\n\
	result         - reads the Result array\n\
	{rw}reg        - reads or writes an arbitary CAM Internal Register\n\
	{rw}scr        - reads or writes the System Configuration Register\n\
	ridr           - reads the Identification Register\n\
	{rw}ssrN       - reads or writes the Segment Select Register N\n\
	                 where N ranges from 0 to 1.\n\
	{rw}lar        - reads or writes the Lookup Allocation Register\n\
	rrsltN         - reads the Result Register N\n\
	                 where N ranges from 0 to 7.\n");

printf("\
	rcmprNN        - reads or writes the Comparand Register NN\n\
	                 where NN ranges from 00 to 15. Note leading zeros\n\
	{rw}gmrNN      - reads or writes the Global Mask Register NN\n\
	                 where NN ranges from 00 to 31. Note leading zeros\n\
	{rw}camspace   - reads or writes the TCAM Data/Mask space\n\
	{rw}entry      - reads or writes the TCAM Data space\n\
	{rw}mask       - reads or writes the TCAM Mask space\n\
	reset          - resets the TCAM \n\
	srchen         - enables the search port \n\
	inhibit        - inhibits the search port \n\
	search         - searches for an entry(72/144/288/576)in the TCAM \n\
	learn          - learn the value(72/144) from a previous search\n\
	                 Does not check for duplicate entries.\n\
	init           - initialises the TCAM. Performs many operations\n\
	                 until the cam is ready for learning and searching.\n\
	dbg            - prints the register array content \n\
	\n\
	{rw}<command> means preface command with either r for read,\n\
	  or w for write.\n\
	\"<command> ?\" returns the arguments needed for that command, e.g.\n\
	\"rcsr ?\" will describe how to use the rcsr command.\n");
}


static void
cmd_quit(int argc, char *argv[])
{
	if (argc > 1)
	{
		/* any argument prints help message. Lazy parsing. */
		printf(
			"quit\n"
			"Quits the shell and returns you to your mundane existence.\n");
		return;
	}

	dag_write_history(history_filename_buf);

	printf("\tGoodbye\n");

	exit(EXIT_SUCCESS);
}


static void
cmd_verbose(int argc, char *argv[])
{
	int n = 0xf;
	
	if ((argc > 1 && argv[1][0] == '?') || (argc > 2))
	{
		printf("Usage: verbose [n]\n\
[n] : if n = 1, enable verbosity. if n = 0 disable verbosity.\
if n not specified, print current status.\n");
		return;
	}

	if (argc > 1)
	{
		/* set verbosity */
		if (argv[1][0] == '1' || argv[1][0] == '0')
		{
			n = strtoul(argv[1],NULL,16);
			dagutil_set_verbosity(n);
		}
	}
	else
	{
		/* print verbosity */
		printf("\tVerbose debugging is %s\n", (dagutil_get_verbosity()) ? "enabled" : "disabled");
	}
}


static void
cmd_csr(int argc, char *argv[])
{
	uint32_t reg_val0, reg_val1, reg_val2;

	if ((argc > 1 && argv[1][0] == '?'))
	{
		/* read needs 1, write needs 2 */
		printf("Usage: rcsr \n\
displays the 32-bit contents of the CSR0, CSR1, CSR2.\n");
		return;
	}

	reg_val0 = read_reg(SC256_CSR0);
	printf("Contents of CSR0: %08x\n",reg_val0);
	reg_val1 = read_reg(SC256_CSR1);
	printf("Contents of CSR1: %08x\n",reg_val1);
	reg_val2 = read_reg(SC256_CSR2);
	printf("Contents of CSR2: %08x\n",reg_val2);

	printf("ssel: %x cmpr: %x rslt: %x bcnt: %x\n", ((reg_val0 >> 26) & 0x7), ((reg_val0 >> 23) & 0x7), ((reg_val0 >> 20) & 0x7), (reg_val0 & 0xfffff));

	
	printf("inst: %x gmsk: %x ltin: %x accs: %x addr: %x\n", ((reg_val1 >> 28) & 0x7), ((reg_val1 >> 24) & 0xf), ((reg_val1 >> 22) & 0x3), ((reg_val1 >> 20) & 0x3), (reg_val1 & 0xfffff));

	
	printf("reset: %x srchen: %x inhibit: %x busy: %x ID: %x\n", ((reg_val2 >> 31) & 0x1), ((reg_val2 >> 30) & 0x1), ((reg_val2 >> 29) & 0x1), ((reg_val2 >> 28) & 0x1), ((reg_val2 >> 24) & 0xf));
	printf("c576: %x c288: %x c144: %x brst_action: %x action: %x\n", ((reg_val2 >> 23) & 0x1), ((reg_val2 >> 22) & 0x1), ((reg_val2 >> 21) & 0x1), ((reg_val2 >> 1) & 0x1), (reg_val2 & 0x1));
}


static void
cmd_wcsr0(int argc, char *argv[])
{
	uint32_t reg_val;

	if ((argc < 2) || (argc > 1 && argv[1][0] == '?'))
	{
		/* read needs 1, write needs 2 */
		printf("Usage: wcsr0 <data> \n\
data - 32-bit value to write to the CSR0.\n");
		return;
	}

	reg_val = strtoul(argv[1], NULL, 16);
	write_reg(SC256_CSR0,reg_val);
}


static void
cmd_wcsr1(int argc, char *argv[])
{
	uint32_t  reg_val;
	if ((argc < 2) || (argc > 1 && argv[1][0] == '?'))
	{							/* read needs 1, write needs 2 */
		printf("Usage: wcsr1 <data> \n\
data - 32-bit value to write to the CSR1.\n");
		return;
	}

	reg_val = strtoul(argv[1], NULL, 16);
	write_reg(SC256_CSR1,reg_val);
}


static void
cmd_wcsr2(int argc, char *argv[])
{
	uint32_t reg_val;

	if ((argc < 2) || (argc > 1 && argv[1][0] == '?'))
	{
		/* read needs 1, write needs 2 */
		printf("Usage: wcsr2 <data>\n\
data - 32-bit value to write to the CSR2.\n");
		return;
	}

	reg_val = strtoul(argv[1], NULL, 16);
	write_reg(SC256_CSR2,reg_val);
}


static void
cmd_val_array(int argc, char *argv[])
{
	uint32_t rw;
	bit72_word_t reg_val;

	rw = (argv[0][0] == 'w');
	

	if (((argc < 2) && rw) || (argc > 1 && argv[1][0] == '?'))
	{
		/* read needs 1, write needs 2 */
		if (rw)
		{
			// XXXX: parse72
			printf("Usage: wval <data2> <data1> <data0> \n\
data2 - higher 8-bit value (of the 72-bit data).\n\
data1 - middle 32-bit value (of the 72-bit data).\n\
data0 - lower 32-bit value (of the 72-bit data).\n");
			
		}
		else
		{
			// XXXX: improve
			printf("Usage: rval \n\
displays <data2> <data1> <data0>\n\
data2 - displays higher 8-bit value (of the 72-bit data).\n\
data1 - displays middle 32-bit value (of the 72-bit data).\n\
data0 - displays lower 32-bit value (of the 72-bit data).\n");

		}
		return;
	}

	if (rw)
	{
		reg_val = parse72( argc, argv, 1, NULL );
		if ( reg_val.valid )
			wval( reg_val );
	}
	else
	{
		reg_val = rval();
		printf("Contents of Value Array: %02x %08x %08x%s\n ",reg_val.data2,reg_val.data1,reg_val.data0, (reg_val.valid)?"":" (INVALID!)");
	}
}


static void 
cmd_result_reg(int argc, char *argv[])
{

	uint32_t reg_val2, reg_val1, reg_val0;

	if ((argc > 1))
	{
		// XXXX: improve
		printf("Usage: result \n\
displays <data2> <data1> <data0>\n\
data2 - displays higher 32-bit value.\n\
data1 - displays middle 32-bit value.\n\
data0 - displays lower 32-bit value.\n");
		return;
	}
	
	 reg_val0 = read_reg(SC256_RES_ARRAY);
	 reg_val1 = read_reg(SC256_RES_ARRAY + 1);
	 reg_val2 = read_reg(SC256_RES_ARRAY + 2);
		printf("Contents of Result Array:  %08x %08x %08x\n",reg_val2, reg_val1, reg_val0);
		printf("valid: %x rdack: %x mmout: %x hitack: %x\n", ((reg_val2 >> 11) & 0x1), ((reg_val2 >> 10) & 0x1), ((reg_val2 >> 9) & 0x1), ((reg_val2 >> 8) & 0x1));
		printf("index: %x\n", (reg_val0 & 0xfffff));
}


static void
cmd_wbrst(int argc, char *argv[])
{
	bit72_word_t reg_val;
	uint16_t addr, brst_cnt, accs;
	if ((argc < 4) || ((argc > 1) && (argv[1][0] == '?')))
	{ // XXXX: parse72
		printf("Usage: wbrst <space> <addr> <bcnt> <data2> <data1> <data0> \n\
space - data space(0) or mask space(1). \n\
addr  - start address for burst write.\n\
bcnt  - number of entries to write on a burst.\n\
data2 - higher 8-bit value.\n\
data1 - middle 32-bit value.\n\
data0 - lower 32-bit value.\n");
		return;
	}
	accs = strtoul(argv[1],NULL,16);
	addr = strtoul(argv[2],NULL,16);
	brst_cnt = strtoul(argv[3],NULL,16);
	reg_val = parse72( argc, argv, 4, NULL );
	if ( !reg_val.valid )
		return;
	
	wval( reg_val );
	write_reg(SC256_CSR0,(brst_cnt));
	write_reg(SC256_CSR1,((1 << 28) | (accs << 20) | addr));
	write_reg(SC256_CSR2,0x2);
	
	while (read_reg(SC256_CSR2) & 0x2);
	printf("Burst write completed\n");
}


static void
cmd_wcam_reg(int argc, char *argv[])
{
	bit72_word_t reg_val;
	uint8_t addr = MAX_REGN;
	uint8_t offset;

	if (!strcmp(argv[0],"wreg") || !strcmp(argv[0],"t")) {
		if ((argc < 3) || ((argc > 1) && (argv[1][0] == '?')))
		{ // XXXX: parse72
				printf("Usage: %s <addr> <data2> <data1> <data0> \n\
addr  - number or symbol of CAM register to write to\n\
data2 - higher 8-bit value.\n\
data1 - middle 32-bit value.\n\
data0 - lower 32-bit value.\n", argv[0]);
			return;
		}
		if ( MAX_REGN == (addr = findn_reg( argv[1] )) ) {
			printf("\nString \"%s\" not recognised as a valid register number or symbol.\n",  argv[1] );
			return;
			
		}
		offset = 2;
	} else {
		if ((argc < 2) || ((argc > 1) && (argv[1][0] == '?')))
		{ // XXXX: parse72
			printf("Usage: %s <data2> <data1> <data0> \n\
data2 - higher 8-bit value.\n\
data1 - middle 32-bit value.\n\
data0 - lower 32-bit value.\n",argv[0]);
			return;
		}
		if ( MAX_REGN == (addr = findn_reg(&(argv[0][1]))) ) {
			printf("\nString \"%s\" not recognised as a valid register number or symbol.\n",  &(argv[0][1]) );
			return;
			
		}
		offset = 1;
	}
	reg_val = parse72( argc, argv, offset, NULL );
	if ( !reg_val.valid )
		return;
		
	write_cam_reg(addr,reg_val);
}


static void
cmd_rcam_reg(int argc, char *argv[])
{
	bit72_word_t reg_val;
	uint8_t addr = 0xff;
	char str[25];
	unsigned long regnum = 0;
	uint8_t i, needhelp = 0, disp;

	if (!strcmp(argv[0],"rreg") || !strcmp(argv[0],"f")) {
		if ((argc < 2) || ((argc > 1) && (argv[1][0] == '?')))
		{
				printf("Usage: %s <addr>\n\
addr - number or symbol of CAM register to read contents of\n", argv[0]);
				needhelp = 1;
		} else {
			if ( MAX_REGN == (addr = findn_reg( argv[1] )) ) {
				printf("\nString \"%s\" not recognised as a valid register number or symbol.\n",  argv[1] );
				needhelp = 1;
				
			}
		}
	} else {
		if ((argc != 1) || ((argc > 1) && (argv[1][0] == '?'))) {
			needhelp = 1;
		} else {
			if ( MAX_REGN == (addr = findn_reg(&(argv[0][1]))) ) {
				printf("\nString \"%s\" not recognised as a valid register number or symbol.\n",  &(argv[0][1]) );
				needhelp = 1;
				
			}
		}
	}
	if ( 0 != needhelp ) {
		printf("Usage: r<addr>\n\
addr - number or symbol of CAM register to read contents of;\n\n\
displays contents of CAM register at location addr; valid symbols for addr are:\n");
		for ( i=0, disp=0; i < MAX_REGN; i++ ) {
			if ( i == 3 || i == 5 || i == 7 ) {
				printf ( "       " );
			} else {
				printf( "%6s ", regname[i] );
			}
			if ( 8 == ++disp ) {
				printf( "\n" );
				disp=0;
			}
		}
		printf( "\n" );
		return;
	}

	/* XXXX: fixme!
	 */
	if (!strcmp(argv[0],"rscr"))
	{
		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: rscr\n\
displays 72-bits of System Configuration Register.\n");
			return;
		}
		reg_val = read_cam_reg(SCR);
		printf("Contents of SCR: %02x %08x %08x\n",reg_val.data2,reg_val.data1,reg_val.data0);
		printf("NSE enable: %x\n",(reg_val.data0 & 0x1));
	}
	else if (!strcmp(argv[0],"ridr"))
	{
		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: ridr\n\
displays 72-bits of Identification Register.\n");
			return;
		}
		reg_val = read_cam_reg(IDR);
		printf("Contents of IDR: %02x %08x %08x\n",reg_val.data2,reg_val.data1,reg_val.data0);
		printf("Mfgr: %x Revision: %x Size: %dK\n",(reg_val.data0 & 0xffff),((reg_val.data0 >> 16) & 0xf),((reg_val.data0 >> 20) & 0x1f) * 16);
	}
	else if (!strcmp(argv[0],"rssr0"))
	{
		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: rssr0\n\
displays 72-bits of Segment Select Register0.\n");
			return;
		}
		reg_val = read_cam_reg(SSR0);
		printf("Contents of SSR0: %02x %08x %08x\n",reg_val.data2,reg_val.data1,reg_val.data0);
		printf("SegSel0: %x SegSel1: %x Segsel2: %x SegSel3: %x\n",((reg_val.data0)& 0xffff),((reg_val.data0 >> 16) & 0xffff),((reg_val.data1)& 0xffff),((reg_val.data1 >> 16) & 0xffff));
	}
	else if (!strcmp(argv[0],"rssr1"))
	{
		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: rssr1\n\
displays 72-bits of Segment Select Register1.\n");
			return;
		}
		reg_val = read_cam_reg(SSR1);
		printf("Contents of SSR1: %02x %08x %08x\n",reg_val.data2,reg_val.data1,reg_val.data0);
		printf("SegSel4: %x SegSel5: %x Segsel6: %x SegSel7: %x\n",((reg_val.data0)& 0xffff),((reg_val.data0 >> 16) & 0xffff),((reg_val.data1)& 0xffff),((reg_val.data1 >> 16) & 0xffff));
	}
	else if (!strcmp(argv[0],"rlar"))
	{
		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: rlar\n\
displays 72-bits of Lookup Allocation Register.\n");
			return;
		}
		reg_val = read_cam_reg(LAR);
		printf("Contents of LAR: %02x %08x %08x\n",reg_val.data2,reg_val.data1,reg_val.data0);
		printf("72bit Segment Enables: %x\n",((reg_val.data0)& 0xffff));
		printf("144bit Segment Enables: %x\n",((reg_val.data0 >> 16) & 0xffff));
		printf("288bit Segment Enables: %x\n",((reg_val.data1)& 0xffff));
		printf("576bit Segment Enables: %x\n",((reg_val.data1 >> 16) & 0xffff));
	}
	else if (!strncmp(argv[0],"rrslt",5))
	{
		strncpy(str,&argv[0][5],1);
		str[1]='\0';
		regnum = strtoul(str,NULL,10);
		if (regnum > 7)
		{
			printf("\nValid Result Register numbers are 0 - 7\n");
			return;
		}
		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: rrslt%ld\n\
displays 72-bits of Result Register%ld.\n",regnum,regnum);
			return;
		}
		
		addr = regnum + RSLT0;
		reg_val = read_cam_reg(addr);
		printf("Contents of RSLT%ld: %02x %08x %08x\n",regnum,reg_val.data2,reg_val.data1,reg_val.data0);
		printf("Hit Result: %x Lookup type: %x Instruction: %x Address: %x \n",(reg_val.data0 >> 31) & 0x1,(reg_val.data0 >> 19) & 0x3,(reg_val.data0 >> 16) & 0x7,reg_val.data0 & 0xffff);
		
	}
	else if (!strncmp(argv[0],"rcmpr",5))
	{
		if (strlen(argv[0]) < 7)
		{
			strncpy(str,&argv[0][5],1);
			str[1]='\0';
		}
		else
		{
			strncpy(str,&argv[0][5],2);
			str[2]='\0';
		}
		regnum = strtoul(str,NULL,10);

		if (regnum > 15)
		{
			printf("\nValid Comparand Register numbers are 0 - 15\n");
			return;
		}

		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: rcmpr%ld\n\
displays 72-bits of Comparand Register%ld.\n",regnum,regnum);
			return;
		}

		addr = regnum + CMPR00;
		reg_val = read_cam_reg(addr);
		printf("Contents of CMPR%ld: %02x %08x %08x \n",regnum,reg_val.data2,reg_val.data1,reg_val.data0);
	}
	else if (!strncmp(argv[0],"rgmr",4))
	{
		if (strlen(argv[0]) < 6)
		{
			strncpy(str,&argv[0][4],1);
			str[1]='\0';
		}
		else
		{
			strncpy(str,&argv[0][4],2);
			str[2]='\0';
		}

		regnum = strtoul(str,NULL,10);

		if (regnum > 31)
		{
			printf("\nValid Global Mask Register numbers are 0 - 31");
			return;
		}

		if ((argc > 1) && (argv[1][0] == '?'))
		{
			printf("Usage: rgmr%ld\n\
displays 72-bits of Global Mask Register%ld.\n",regnum,regnum);
			return;
		}

		addr = GMR00 + regnum;
		reg_val = read_cam_reg(addr);
		printf("Contents of GMR%ld: %02x %08x %08x \n",regnum,reg_val.data2,reg_val.data1,reg_val.data0);
	}
}


static void
cmd_cam_space(int argc, char *argv[])
{
	uint8_t accs, inst;
	uint8_t rw, ie, im, offset;
	uint16_t addr;
	bit72_word_t reg_val;
	
	rw = (argv[0][0] == 'w');
	im = (argv[0][1] == 'm');
	ie = (argv[0][1] == 'e');
	
	if (rw)
	{
		if (im) { // explicit mask space
			if ((argc < 3) || (argc > 1 && argv[1][0] == '?'))
			{ // XXXX: parse72
				printf("Usage: wmask <addr> <data2> <data1> <data0>\n\
addr  - address of the mask entry to be written.\n\
data2 - higher 8-bit value.\n\
data1 - middle 32-bit value.\n\
data0 - lower 32-bit value.\n");
				return;
			}
			offset = 1;
			accs = 1;
		} else if (ie) { // explicit data space
			if ((argc < 3) || (argc > 1 && argv[1][0] == '?'))
			{ // XXXX: parse72
				printf("Usage: wentry <addr> <data2> <data1> <data0>\n\
addr  - address of the data entry to be written.\n\
data2 - higher 8-bit value.\n\
data1 - middle 32-bit value.\n\
data0 - lower 32-bit value.\n");
				return;
			}
			offset = 1;
			accs = 0;
		} else {
			if ((argc < 4) || (argc > 1 && argv[1][0] == '?'))
			{ // XXXX: parse72
				printf("Usage: wcamspace <space> <addr> <data2> <data1> <data0>\n\
space - data space(0) or mask space(1). \n\
addr  - address of the entry to be written.\n\
data2 - higher 8-bit value.\n\
data1 - middle 32-bit value.\n\
data0 - lower 32-bit value.\n");
				return;
			}
			offset = 2;
			accs = strtoul(argv[1],NULL,16);
		}
		inst = 1;
		addr = strtoul(argv[offset],NULL,16);
		reg_val = parse72( argc, argv, offset+1, NULL );
		if ( !reg_val.valid )
			return;

		wval(reg_val);
		write_reg(SC256_CSR0,0);
		write_reg(SC256_CSR1,((inst << 28) | (accs << 20) | addr));
		write_reg(SC256_CSR2,1);
	}
	else
	{
		if (im) { // explicit mask space
			if ((argc < 2) || (argc > 1 && argv[1][0] == '?'))
			{ // XXXX: improve
				printf("Usage: rmask <addr>\n\
addr  - address of the mask entry to be read.\n\
displays 72-bit value of TCAM mask space at the chosen address.\n");
				return;
			}
			offset = 1;
			accs = 1;
		} else if (ie) { // explicit data space
			if ((argc < 2) || (argc > 1 && argv[1][0] == '?'))
			{ // XXXX: improve
				printf("Usage: rentry <addr>\n\
addr  - address of the data entry to be read.\n\
displays 72-bit value of TCAM data space at the chosen address.\n");
				return;
			}
			offset = 1;
			accs = 0;
		} else {
			if ((argc < 3) || (argc > 1 && argv[1][0] == '?'))
			{ // XXXX: improve
				printf("Usage: rcamspace <space> <addr>\n\
space - data space(0) or mask space(1). \n\
addr  - address of the entry to be read.\n\
displays 72-bit value of TCAM space.\n");
				return;
			}
			offset = 2;
			accs = strtoul(argv[1],NULL,16);
		}
		inst = 0;
		addr = strtoul(argv[offset],NULL,16);

		write_reg(SC256_CSR0,0);
		write_reg(SC256_CSR1,((inst << 28) | (accs << 20) | (addr)));
		write_reg(SC256_CSR2,1);

		if (accs)
			printf("Contents of Mask space entry(%x):\n",addr);
		else
			printf("Contents of Data space entry(%x):\n",addr);

		printf("%02x %08x %08x\n",(read_reg(SC256_RES_ARRAY+2) & 0xff),read_reg(SC256_RES_ARRAY+1),read_reg(SC256_RES_ARRAY));

	}
}

static void
cmd_reset(int argc, char *argv[])
{
	if (argc > 1)
	{
		// XXXX: improve
		printf("Usage: reset \nresets the TCAM.\n");
		return;
	}
	write_reg(SC256_CSR2,0x80000000);
#if USE_NANOSLEEPS
	dagutil_nanosleep(640); /* can be dropped down to 320ns on RevB cards */
#endif
}

static void
cmd_srchen(int argc, char *argv[])
{
	if (argc > 1)
	{ // XXXX: improve
		printf("Usage: srchen\nenables the search port.\n");
		return;
	}
	write_reg(SC256_CSR2,0x40000000);
	printf("Firmware search port enabled\n");
}

static void
cmd_inhibit(int argc, char *argv[])
{
	if (argc > 1)
	{ // XXXX: improve
		printf("Usage: inhibit\ninhibits the search port.\n");
		return;
	}
	write_reg(SC256_CSR2,0x20000000);
	printf("\nFirmware search port inhibited\n");
}

static void
cmd_search(int argc, char *argv[])
{
	uint8_t prn_usage = 0, rslt=0, ssel=0, cmpr =0, gmsk =0, loop_var, iter=0, ltin=0;
	uint32_t  srch_data[18], width=0, reg_val0 = 0, reg_val1 = 0, reg_val2 = 0;
		
	if (argc <= 1 || (argc > 1 && argv[1][0] == '?'))
		prn_usage = 1;
	else
	{
		width = strtoul(argv[1],NULL,0);
		switch(width)
		{
			case 72:
				if (argc < 9)
						prn_usage = 1;
				break;
			case 144:
				if (argc < 11)
					prn_usage = 1;
				break;
			case 288:
			  		if (argc < 15)
						prn_usage = 1;
				break;
			case 576:
					if (argc < 24)
						prn_usage = 1;
				break;
			default:
				printf("\nInvalid width. Valid widths are 72, 144, 288, 576\n");
				return;
		}
	}
				
	if (prn_usage)
	{ // XXXX: improve
		prn_usage = 0;
		printf("Usage: search <width> <ssel> <cmpr> <rslt> <gmsk> <search_dataN> <search_dataN-1> .... <search_data0> \n\
width - specifies the width (72-bit, 144-bit, 288-bit, 576 bit) of search_data.\n\
ssel  - specifies which segment to search.\n\
cmpr  - specifies which comparand register to use.\n\
rslt  - specifies which result register to use.\n\
gmsk  - specifies which mask register to use.\n\
search_data  - 32-bit search data pattern.\n\
search_data0 - refers LSBs\n\
search_dataN - refers MSBs\n\
where N ranges from 3 to 18 depending upon search width.\n");
		return;
	}
	else
	{
		ssel = strtoul(argv[2],NULL,16) & 0x7;
		cmpr = strtoul(argv[3],NULL,16) & 0x7;
		rslt = strtoul(argv[4],NULL,16) & 0x7;
		gmsk = strtoul(argv[5],NULL,16) & 0xf;
		// XXXX: parse576
		iter = width / 32;
		if (iter%32)
			iter+=1;

		for (loop_var = iter;loop_var > 0;loop_var--)
		{
			srch_data[loop_var-1] = strtoul(argv[6+iter-loop_var],NULL,16);
		}

		if (width == 72)
			printf("DBG: User said: Search%u ssel=%.4x gmsk=%.4x cmpr=%.4x rslt=%.4x for .. %.8x %.8x %.8x \n", width, ssel, gmsk, cmpr, rslt,
			srch_data[0], srch_data[1], srch_data[2]);
		else if (width == 144)
			printf("DBG: User said: Search%u ssel=%.4x gmsk=%.4x cmpr=%.4x rslt=%.4x for .. %.8x %.8x %.8x %.8x %.8x \n", width, ssel, gmsk, cmpr, rslt,
			srch_data[0], srch_data[1], srch_data[2], srch_data[3], srch_data[4]);
		else if (width == 288)
			printf("DBG: User said: Search%u ssel=%.4x gmsk=%.4x cmpr=%.4x rslt=%.4x for .. %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", width, ssel, gmsk, cmpr, rslt,
			srch_data[0], srch_data[1], srch_data[2], srch_data[3],
			srch_data[4], srch_data[5], srch_data[6], srch_data[7], srch_data[8]);
		else if (width == 576)
			printf("DBG: User said: Search%u ssel=%.4x gmsk=%.4x cmpr=%.4x rslt=%.4x for .. %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n", width, ssel, gmsk, cmpr, rslt,
			srch_data[0], srch_data[1], srch_data[2], srch_data[3],
			srch_data[4], srch_data[5], srch_data[6], srch_data[7],
			srch_data[8], srch_data[9], srch_data[10], srch_data[11],
			srch_data[12], srch_data[13], srch_data[14], srch_data[15],
			srch_data[16], srch_data[17]);

		for (loop_var = 0; loop_var < iter; loop_var++) // range check me please. should be 0 - 17 inclusive
		{
			write_reg(SC256_VAL_ARRAY + loop_var, srch_data[loop_var]);
		}

		ltin = (width / 72) - 1;

		write_reg(SC256_CSR0,((ssel << 26) | (cmpr << 23) | (rslt << 20)));
		write_reg(SC256_CSR1,((2 << 28) | (gmsk << 24) | (ltin << 22)));
		write_reg(SC256_CSR2,0x1); //Set action bit.
	}//else

#if USE_NANOSLEEPS
	dagutil_nanosleep(220); /* can be dropped down to 110ns on RevB cards */
#endif

	printf("%d-bit search result:\n",width);

	reg_val0 = read_reg(SC256_RES_ARRAY);
	reg_val1 = read_reg(SC256_RES_ARRAY + 1);
	reg_val2 = read_reg(SC256_RES_ARRAY + 2);
		printf("Contents of Result Array:  %08x %08x %08x\n",reg_val2, reg_val1, reg_val0);
		printf("valid: %x rdack: %x mmout: %x hitack: %x\n", ((reg_val2 >> 11) & 0x1), ((reg_val2 >> 10) & 0x1), ((reg_val2 >> 9) & 0x1), ((reg_val2 >> 8) & 0x1));
		printf("index: %x\n", (reg_val0 & 0xfffff));
}


static void
cmd_learn(int argc, char *argv[])
{
	uint8_t rslt=0, ssel=0, cmpr =0, gmsk =0;
	uint32_t width=0, reg_val0 = 0, reg_val1 = 0, reg_val2 = 0;
	
	if ((argc < 6) || (argc > 1 && argv[1][0] == '?'))
	{ // XXXX: improve
        printf("Usage: learn <width> <ssel> <cmpr> <rslt> <gmsk> \n\
width - specifies the width (72-bit, 144-bit) of data to be stored in the TCAM.\n\
ssel  - specifies which segment to search.\n\
cmpr  - specifies which comparand register to use.\n\
rslt  - specifies which result register to use.\n\
gmsk  - specifies which mask register to use.\n\
\n\
Note - Learn operation is valid only if it is performed after a search.\n");
	}
	else
	{
		width = strtoul(argv[1],NULL,0);
		if (!(width == 72 || width == 144))
		{
			printf("\nValid widths for learn operations are 72, 144.\n");
			return;
		}
		ssel = strtoul(argv[2],NULL,16);
		cmpr = strtoul(argv[3],NULL,16);
		rslt = strtoul(argv[4],NULL,16);
		gmsk = strtoul(argv[5],NULL,16);

		write_reg(SC256_CSR0, ((ssel << 26) | (cmpr << 23) | (rslt << 20)));
		write_reg(SC256_CSR1, ((3 << 28) | (gmsk << 24) | (width << 22)));
		write_reg(SC256_CSR2,0x1); //Set action bit.
	}
#if USE_NANOSLEEPS
	dagutil_nanosleep(220); /* can be dropped down to 110ns on RevB cards */
#endif

	printf("%d-bit search result:\n",width);

	reg_val0 = read_reg(SC256_RES_ARRAY);
	reg_val1 = read_reg(SC256_RES_ARRAY + 1);
	reg_val2 = read_reg(SC256_RES_ARRAY + 2);
		printf("Contents of Result Array:  %08x %08x %08x\n",reg_val2, reg_val1, reg_val0);
		printf("valid: %x rdack: %x mmout: %x hitack: %x\n", ((reg_val2 >> 11) & 0x1), ((reg_val2 >> 10) & 0x1), ((reg_val2 >> 9) & 0x1), ((reg_val2 >> 8) & 0x1));
		printf("index: %x\n", (reg_val0 & 0xfffff));
}


static void
cmd_init(int argc, char *argv[])
{
	bit72_word_t reg_val;
	int offset, more;
	
	if (argc < 4 || (argc > 1 && argv[1][0] == '?'))
	{ // XXXX: parse72
		printf("Usage: init <lar_data2> <lar_data1> <lar_data0> <ssr0_data2> <ssr0_data1> <ssr0_data0> <ssr1_data2> <ssr1_data1> <ssr1_data0> \n\
lar_dataN  - constitutes 72-bit value with which LAR has to be initialised.\n\
ssr0_dataN - constitutes 72-bit value with which SSR0 has to be initialised.\n\
ssr1_dataN - constitutes72-bit value with which SSR1 has to be initialised.\n");
		return;
	}
	// Reset the CAM
	write_reg(SC256_CSR2, 0x80000000);
	dagutil_nanosleep(640); /* can be dropped down to 320ns on RevB cards */

	// Blank the Data space

	write_reg(SC256_VAL_ARRAY, 0);
	write_reg(SC256_VAL_ARRAY + 1, 0);
	write_reg(SC256_VAL_ARRAY + 2, 0);

	write_reg(SC256_CSR0,0xffff);
	write_reg(SC256_CSR1,0x10000000);
	write_reg(SC256_CSR2,0x2);
	
	while (read_reg(SC256_CSR2) & 0x2);

	// Blank the Mask space

	write_reg(SC256_VAL_ARRAY, 0xffffffff);
	write_reg(SC256_VAL_ARRAY + 1, 0xffffffff);
	write_reg(SC256_VAL_ARRAY + 2, 0xff);

	write_reg(SC256_CSR0,0xffff);
	write_reg(SC256_CSR1,0x10100000);
	write_reg(SC256_CSR2,0x2);
	
	while (read_reg(SC256_CSR2) & 0x2);

	// Init LAR
	offset = 1;
	more = 0;
	reg_val = parse72( argc, argv, offset, &more );
	if ( !reg_val.valid )
		return;
	write_cam_reg(LAR,reg_val);

	// Init SSR0
	offset+=more;
	more = 0;
	reg_val = parse72( argc, argv, offset, &more );
	if ( !reg_val.valid )
		return;
	write_cam_reg(SSR0,reg_val);

	// Init SSR1
	offset+=more;
	more = 0;
	reg_val = parse72( argc, argv, offset, &more );
	if ( !reg_val.valid )
		return;
	write_cam_reg(SSR1,reg_val);

	//Init SCR
	reg_val.data2 = 0;
	reg_val.data1 = 0;
	reg_val.data0 = 0x81;
	reg_val.valid = 1;
	write_cam_reg(SCR,reg_val);
}


static void
cmd_dbg(int argc, char *argv[])
{
	uint32_t reg_val = 0x2100;
	uint32_t n = 0;
	uint32_t i;
	
	 // XXXX: usage text
	n = dagutil_get_verbosity();
	dagutil_set_verbosity(0);
	printf("\n");
	for (i = 0; i < 32; i += 4)
	{
		printf("%08x: %08x %08x %08x %08x\n", reg_val, read_reg(i), read_reg(i+1), read_reg(i+2), read_reg(i+3));
		reg_val = reg_val + 16;
	}
	dagutil_set_verbosity(n);
}


/*
 * This is a wrapper around readline which cannot handle changing
 * stdin in any way.
 */
static char *
dgetline(char *prompt)
{
	static char *buf;

	if (1 == uReadFromKeyboard)
	{
		if (0 < strlen(command_buf))
		{
			/* Use the -c parameter from the command line */
			if (0 == uHaveReadCommandLine)
			{
				uHaveReadCommandLine = 1;
				
				/* Return a malloc()d string so all strings returned by dgetline are malloc()d. */
				return (char*) dagutil_strdup((const char*) command_buf);
			}
			else
			{
				/* Already read the command line option */
				return NULL;
			}
		}
		else
		{
#if HAVE_EDITLINE
			/* Get the next line from readline(). */
			return readline(prompt);
#else
			/* Get the next line via standard I/O. */
			printf("%s", prompt);
#endif /* HAVE_EDITLINE */
		}
	}

	/* Note: do _not_ prompt or echo here. */
	buf = dagutil_malloc(BUFSIZ);
	if (buf == NULL)
		dagutil_panic("dgetline: out of memory\n");

	return fgets(buf, BUFSIZ, stdin);
}


#ifndef ENDACE_UNIT_TEST
int
main(int argc, const char* const * argv)
{
	return dagcam256_main(argc, (char**) argv);
}
#endif /* ENDACE_UNIT_TEST */
