/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

/*
 * generate 'microcode dispatch table' from instruction list
 *
 * Usage is:
 *  geni [-mpv] instruction-table
 *
 * where:
 *	-m 	generate the index table for the micro-op profiler 'mprof'
 *	-p	generate the index table for the op profiler 'cprof'
 *	-c	generate the name table for both profilers
 *	-v	verbose
 *	-s	generate special functions
 *
 * The instruction table format is described in ../ins.tab.
 */
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <string.h>
# include <unistd.h>
# include <errno.h>
# ifdef HAVE_GETOPT_H
#  include <getopt.h>
# endif

# include <begemot.h>
# include "cdefs.h"
# include "util.h"

RCSID("$Id: geni.c 476 2001-07-09 08:09:10Z hbb $")

# define HASHSIZE	10007
# define HASHJUMP	17

int	h_insert(char *);

void	fill(u_int, u_int, char *);
void	trans(int, int, char **);
void	appfunc(char *);
char	*strerror(int);

char	*hashtab[HASHSIZE];	/* name table */
int	hashsize;		/* number of names in table */

int 	xcode;			/* current instruction code */
int	xccc;			/* current microinstruction count */
int	profiler_output;
int	spec;
u_int	xtab[0x10000][4];	/* these point into the hash table */
char	*ifile;			/* file name */
u_int	def_ill;		/* ILL function index */
int	check_fill;

void	do0(u_int, char **, int);
void	do1(u_int, char **, int);
void	doQ(u_int, char **, int);
void	doQ0(u_int, char **, int);
void	doA(u_int, char **, int);
void	doS(u_int, char **, int);
void	doJ(u_int, char **, int);
void	doK(u_int, char **, int);
void	doK0(u_int, char **, int);
void	doD(u_int, char **, int);
void	doD0(u_int, char **, int);
void	doR(u_int, char **, int);
void	doR0(u_int, char **, int);
void	doF(u_int, char **, int);
void	doG(u_int, char **, int);
void	doH(u_int, char **, int);
void	doI(u_int, char **, int);
void	doB(u_int, char **, int);

struct {
	char	*key;
	void	(*func)(u_int, char **, int);
} keytab[] = {
	{ "0", do0 },
	{ "1", do1 },
	{ "Q", doQ },
	{ "Q0", doQ0 },
	{ "A", doA },
	{ "S", doS },
	{ "J", doJ },
	{ "K", doK },
	{ "K0", doK0 },
	{ "D", doD },
	{ "D0", doD0 },
	{ "R", doR },
	{ "R0", doR0 },
	{ "F", doF },
	{ "G", doG },
	{ "H", doH },
	{ "I", doI },
	{ "B", doB },
	{ NULL, NULL }
};
int	define(char *);

void	out_c_defs(void);
void	do_profiler_output(void);
void	profiler_out_names(void);

int	ncmp(const void *, const void *);

void	tab_out_profiler(void);
void	tab_out_c(void);

int
main(int argc, char *argv[])
{
	char	buf[1000];
	char	*fields[20];
	u_int	len;
	char	*end;
	int	opt;
	int	base, f;
	int k;

	set_argv0(argv[0]);
	while((opt = getopt(argc, argv, "cvmps")) != EOF)
		switch(opt) {

		  case 'v':
			verb_option("all");
			break;

		  case 'm':
			profiler_output = 1;
			break;

		  case 'p':
			profiler_output = 2;
			break;

		  case 'c':
			profiler_output = 3;
			break;

		  case 's':
			spec = 1;
			break;
	}
	argc -= optind;
	argv += optind;

	if(argc != 1)
		panic("Usage: geni [-mvps] file");

	ifile = argv[0];
	if(strcmp(argv[0], "-")) {
		if(!freopen(argv[0], "r", stdin))
			panic("%s: %s\n", argv[0], strerror(errno));
	} else
		ifile = "stdin";

	def_ill = define("ILL");

	check_fill = 0;
	fill(0, 0200000, "ILL");
	check_fill = 1;

	while(fgets(buf, sizeof(buf), stdin)) {
		if((len = strlen(buf)) > 0 && buf[len-1] == '\n')
			buf[len-1] = 0;
		verb(V_DFLT, 1, "'%s'", buf);
		f = getmfields(buf, fields, 20);
		if(f == 0 || fields[0][0] == '#')
			continue;
		if(f < 3)
			panic("missing fields: %s\n", buf);
		base = strtol(fields[0], &end, 8);
		if(*end)
			panic("syntax: %s\n", buf);

		verb(V_DFLT, 1, "%s", fields[2]);

		if(strlen(fields[1]) > 1 && !spec)
			continue;
		for(k = 0; keytab[k].key; k++)
			if(strcmp(keytab[k].key, fields[1]) == 0) {
				if(strlen(fields[1]) > 1)
					check_fill = 0;
				else
					check_fill = 1;
				(*keytab[k].func)(base, fields+2, f-2);
				break;
			}
		if(keytab[k].key == NULL)
			panic("bad type key: %s\n", fields[1]);
	}
	define("FILL");
	define("ret");

	switch(profiler_output) {

	  case 3:
		do_profiler_output();
		break;

	  case 2:
	  case 1:
		profiler_out_names();
		tab_out_profiler();
		break;

	  case 0:
		tab_out_c();
		break;
	}

	return 0;
}

/*
 * output C-code
 */
void
tab_out_c()
{
	u_int code, ccc;

	out_c_defs();
	printf("void (*const instab[0200000][4])(void) = {\n");
	for(code = 0; code < 0x10000; code++) {
		printf("/* %06o */ { ", code);
		for(ccc = 0; ccc < 4; ccc++)
			printf("%s%s", hashtab[xtab[code][ccc]], (ccc < 3) ? ", " : "");
		printf(" },\n");
	}
	printf("};\n");
}

/*
 * output C-code for profiler
 */
void
tab_out_profiler()
{
	u_int code, ccc;

	printf("int instab[0200000][4] = {\n");
	for(code = 0; code < 0x10000; code++) {
		printf("/* %06o */ { ", code);
		for(ccc = 0; ccc < 4; ccc++)
			printf("%s%s", hashtab[xtab[code][ccc]], (ccc < 3) ? ", " : "");
		printf(" },\n");
	}
	printf("};\n");
}


/*
 * print function prototypes for all symbols
 */
void
out_c_defs()
{
	int	i;

	for(i = 0; i < HASHSIZE; i++)
		if(hashtab[i] != 0)
			printf("void %s(void);\n", hashtab[i]);
}

/*
 * start a new instruction code
 */
static void
start(u_int b)
{
	if(check_fill && xtab[b][0] != def_ill)
		panic("xtab[%u] already filled with '%s,%s,%s,%s'",
			b,
			hashtab[xtab[b][0]], hashtab[xtab[b][1]],
			hashtab[xtab[b][2]], hashtab[xtab[b][3]]);
	xcode = b;
	xccc = 0;
}


/*
 * done with this instruction code, fill microcode slots with returns
 */
static void
theend(void)
{
	while(xccc < 4)
		appfunc("ret");
	xcode++;
}

/*
 * fill instruction code with constant micro-function
 */
void	
fill(u_int from, u_int to, char *w)
{
	while(from < to) {
		start(from++);
		appfunc(w);
		theend();
	}
}

/*
 * one code only
 */
void
do0(u_int base, char **fields, int f UNUSED)
{
	start(base);
	appfunc(fields[0]);
	define(fields[0]);
	theend();
}

/*
 * dito, but do translation
 */
void
do1(u_int base, char **fields, int f)
{
	start(base);
	trans(0, f, fields);
	theend();
}

/* 
 * JMP - register address is illegal
 */
void
doG(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 0100; b++) {
		start(b);
		if((b & 070) == 0)
			appfunc("ILL");
		else
			trans(b, f, fields);
		theend();
	}
}

/*
 * RTS/SPL last 3 bits are arg
 */
void
doQ(u_int base, char **fields, int f UNUSED)
{
	u_int b;

	for(b = base; b < base + 010; b++) {
		start(b);
		appfunc(fields[0]);
		define(fields[0]);
		theend();
	}
}
void
doQ0(u_int base, char **fields, int f UNUSED)
{
	start(base);
	appfunc(fields[0]);
	theend();
}

/*
 * SCC/CCC last 4 bits are arg
 */
void
doA(u_int base, char **fields, int f UNUSED)
{
	u_int b;

	for(b = base; b < base + 020; b++) {
		start(b);
		appfunc(fields[0]);
		define(fields[0]);
		theend();
	}
}

/*
 * single operand
 */
void
doS(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 0100; b++) {
		start(b);
		trans(b, f, fields);
		theend();
	}
}

/*
 * single operand but no mode 0
 */
void
doB(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 0100; b++) {
		start(b);
		if((b & 070) == 0)
			appfunc("ILL");
		else
			trans(b, f, fields);
		theend();
	}
}

/*
 * BRs, TRAP, EMT lower byte is arg
 */
void
doJ(u_int base, char **fields, int f UNUSED)
{
	u_int b;

	for(b = base; b < base + 0400; b++) {
		start(b);
		appfunc(fields[0]);
		define(fields[0]);
		theend();
	}
}

/*
 * JSR 
 */
void
doK(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 01000; b++) {
		start(b);
		if((b & 070) == 0)
			appfunc("ILL");
		else
			trans(b, f, fields);
		theend();
	}
}
void
doK0(u_int base, char **fields, int f UNUSED)
{
	start(base);
	appfunc(fields[0]);
	theend();
}

/*
 * double ops
 */
void
doD(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 010000; b++) {
		start(b);
		trans(b, f, fields);
		theend();
	}
}
void
doD0(u_int base, char **fields, int f UNUSED)
{
	start(base);
	appfunc(fields[0]);
	theend();
}

/*
 * double register and source/dest op
 */
void
doR(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 01000; b++) {
		start(b);
		trans(b, f, fields);
		theend();
	}
}
void
doR0(u_int base, char **fields, int f UNUSED)
{
	u_int b;

	for(b = base; b < base + 0100; b++) {
		start(b);
		appfunc(fields[0]);
		theend();
	}
}

/* 
 * FPP with single float destination
 * mode 0 with register 6 and 7 is illegal
 */
void
doF(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 0100; b++) {
		start(b);
		if((b & 077) == 006 || (b & 077) == 007)
			appfunc("FILL");
		else
			trans(b, f, fields);
		theend();
	}
}

/*
 * FPP - one operand is integer source the other FPP reg.
 */
void
doH(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 0400; b++) {
		start(b);
		trans(b, f, fields);
		theend();
	}
}

/*
 * FPP - two float ops
 * mode 06 and 07 on source is illegal
 */
void
doI(u_int base, char **fields, int f)
{
	u_int b;

	for(b = base; b < base + 0400; b++) {
		start(b);
		if((b & 077) == 006 || (b & 077) == 007)
			appfunc("FILL");
		else
			trans(b, f, fields);
		theend();
	}
}

void
trans(int v, int f, char **fields)
{
	int i;
	char	nbuf[100];

	for(i = 1; i < f; i++) {
		if(fields[i][0] == '%') {
			appfunc(fields[0]);
			define(fields[0]);
		} else switch(fields[i][1]) {
		case '0':	/* DD */
			sprintf(nbuf, "%c%02o", fields[i][0], v & 077);
			if(profiler_output < 2)
				appfunc(nbuf);
			define(nbuf);
			break;
		case '1':	/* SS */
			sprintf(nbuf, "%c%02o", fields[i][0], (v >> 6) & 077);
			if(profiler_output < 2)
				appfunc(nbuf);
			define(nbuf);
			break;
		case '2':	/* FPP */
			sprintf(nbuf, "FPP_%c", fields[i][0]);
			if(profiler_output < 2)
				appfunc(nbuf);
			define(nbuf);
			break;
		default:
			panic("bad translation key: %c\n", fields[i][1]);
		}
	}
}

void
appfunc(char *s)
{
	xtab[xcode][xccc++] = define(s);
}


/*
 * compute a hash code from a string
 */
static u_int
h_code(char *s)
{
	u_int	h = 0;
	u_char *u = (u_char *)s;

	while(*u)
		h = (h << 1) + *u++;
	return h % HASHSIZE;
}

int
h_insert(char *s)
{
	u_int	h0 = h_code(s);
	u_int	h = h0;

	do {
		if(hashtab[h] == NULL) {
			hashtab[h] = xstrsave(s);
			hashsize++;
			return h;
		}
		if(strcmp(hashtab[h], s) == 0)
			return h;
		h = (h + HASHJUMP) % HASHSIZE;
	} while(h != h0);
	return -1;
}

int
define(char *n)
{
	int	 i;

	if((i = h_insert(n)) < 0)
		panic("recompile me with greater HASHSIZE!");
	return i;
}

void
do_profiler_output()
{
	int	i, j;

	printf("char *inames[] = {\n");
	for(i = j = 0; i < HASHSIZE; i++)
		if(hashtab[i] != 0)
			printf("\t\"%s\",	/* %3d */\n", hashtab[i], j++);
	printf("};\n\n");

	printf("# define NNAMES %d\n", hashsize);
}


/*
 * print sorted defines for the names
 */
void
profiler_out_names()
{
	char	**ntab;
	int	i, j;

	ntab = xalloc(sizeof(char *) * hashsize);
	for(j = i = 0; i < HASHSIZE; i++)
		if(hashtab[i] != 0)
			ntab[j++] = hashtab[i];

	qsort(ntab, hashsize, sizeof(char *), ncmp);
	for(i = 0; i < hashsize; i++)
		printf("# define %s %d\n", ntab[i], i);

	free(ntab);
}


int
ncmp(const void *p1, const void *p2)
{
	return strcmp(*(char **)p1, *(char **)p2);
}
