/*
 * File: genop.c
 *
 * Program to generate tables for handling assembler instructions
 * and generating code in IMP80 compiler
 *
 * Bob Eager   August 2002
 *
 */

/*
 * Format of opcode information tables; these are indexed by the arbitrary
 * opcode values assigned in the symbol definitions file, which in turn are
 * simply the order of appearance in the original opcode information file.
 *
 * opcinfo1:	each entry is a 32-bit value, as follows (not to scale):
 *
 *       1 bit   2 bits   5 bits    8 bits      8 bits      8 bits
 *	+------------------------------------------------------------+
 *	| Jump | Spare | Variant | Operand 1 | Operand 2 | Operand 3 |
 *	| flag |       |  code   |   code    |   code    |   code    |
 *	+------------------------------------------------------------+
 *
 * opcinfo2:	each entry is a 32-bit value, as follows (not to scale);
 *		the opcode size is expressed in bytes, with unused fields
 *		set to zero
 *
 *           8 bits         8 bits         8 bits         8 bits   
 *	+------------------------------------------------------------+
 *	|    Opcode    |    Opcode    |    Opcode    |    Opcode     |
 *	|    byte 1    |    byte 2    |    byte 3    |     size      |
 *	+------------------------------------------------------------+
 *
 */

/*
 * Compile time options
 *
 * Define exactly one target language name to be 1; all the rest should be 0.
 *
 */

#define	C	1			/* C language */
#define	IMP	0			/* IMP80 language */


#ifdef	__IBMC__
#pragma	strings(readonly)
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>

#ifndef LIBHASSTRUPR
char *strupr(char *inplace)
{
  int c;
  char *orig = inplace;
  while ((c = *inplace) != '\0') {
    if (isalpha(c) && islower(c)) c = toupper(c); 
    *inplace++ = c;
  }
  return(orig);
}
#endif

#define	FALSE		0
#define	TRUE		1

#define	MAXCC		132		/* Max length of line buffer */
#define	MAXMNEM		8		/* Max length of a mnemonic */
#define	MAXOP		600		/* Max number of opcodes */
#define	MAXRAND		3		/* Max number of operands */

/* Type definitions */

typedef	void		VOID, *PVOID;
typedef	int		BOOL, *PBOOL;
typedef	char		CHAR, *PCHAR;
typedef	unsigned char	UCHAR, *PUCHAR;
typedef	int		INT, *PINT;
typedef	unsigned int	UINT, *PUINT;
typedef	long		LONG, *PLONG;
typedef	unsigned long	ULONG, *PULONG;

/* Forward references */

static	BOOL	do_instr(VOID);
static	BOOL	do_jump(VOID);
static	BOOL	do_mnem(VOID);
static	BOOL	do_operand(INT);
static	VOID	do_tables(PUCHAR, PUCHAR, PUCHAR);
static	BOOL	do_variant(VOID);
static	VOID	error(PUCHAR, ...);
static	FILE	*openio(PUCHAR, PUCHAR);
static	VOID	readline(VOID);
static	VOID	translate(PUCHAR, PUCHAR, PUCHAR);
static	VOID	usage(VOID);
static	VOID	warn(PUCHAR, ...);

/* Local data */

static	UCHAR		cc[MAXCC];	/* Input line buffer		*/
static	INT		errors = 0;	/* Count of errors		*/
static	FILE		*infp;		/* File pointer for input file	*/
static	UINT		instr[MAXOP];	/* Opcode information		*/
static	UINT		jump[MAXOP];	/* Jump flags			*/
static	INT		line = 0;	/* Input line number		*/
static	FILE		*listfp;	/* File pointer for listing file*/
static	UCHAR		mnem[MAXOP][MAXMNEM+1];
					/* Opcode mnemonic text		*/
static	INT		num = 0;	/* Number of opcodes		*/
static	UINT		operand[MAXRAND][MAXOP];
					/* Operand type			*/
static	FILE		*opfp;		/* File pointer for opcode defns*/
static	PUCHAR		progname;	/* Program name			*/
static	INT		size[MAXOP];	/* Opcode size in bytes		*/
static	FILE		*tabfp;		/* File pointer for table file	*/
static	UINT		variant[MAXOP];	/* Opcode variant flag		*/
static	UCHAR		xinstr[MAXCC];	/* Input opcode val temp string */
static	UCHAR		xjump[MAXCC];	/* Input jump flag temp string	*/
static	UCHAR		xmnem[MAXCC];	/* Input mnemonic temp string	*/
static	UCHAR		xoperand[MAXRAND][MAXCC];
					/* Input operand temp strings	*/
static	UCHAR		xvariant[MAXCC];/* Input variant temp string	*/

/* Tables */

#define	MAXMAC		13

static	PUCHAR	macros[MAXMAC] = {
"JUMP(x)\t\t((opcinfo1[x] >> 31) & 0x01)",
"OPB(x,n)\t((unsigned char) ((opcinfo2[x] >> (32-x*8)) & 0xff))",
"OPB1(x)\t\t((unsigned char) ((opcinfo2[x] >> 24) & 0xff))",
"OPB2(x)\t\t((unsigned char) ((opcinfo2[x] >> 16) & 0xff))",
"OPB3(x)\t\t((unsigned char) ((opcinfo2[x] >> 8) & 0xff))",
"OPSIZE(x)\t(opcinfo2[x] & 0xff)",
"RAND1(x)\t((opcinfo1[x] >> 16) & 0xff)",
"RAND2(x)\t((opcinfo1[x] >> 8) & 0xff)",
"RAND3(x)\t(opcinfo1[x] & 0xff)",
"REG8(x)\t\t(AL <= x && x <= BH)",
"REG16(x)\t(AX <= x && x <= DI)",
"REG32(x)\t(EAX <= x && x <= EDI)",
"VARIANT(x)\t((opcinfo1[x] >> 24) & 0x1f)"
};


typedef struct _RANDINFO {
PUCHAR	name;
INT	value;
} RANDINFO;

typedef struct _REGINFO {
PUCHAR	name;
INT	num;
} REGINFO;

#define	MAXREGNAME	42		/* All register names */
#define	MAXREGS		24		/* User-level registers */

/* Note that all register values below must correspond to the
   hardware encodings in the least significant three bits. In addition,
   the user-level registers must come first, MAXREGS and MAXREGNAME must
   reflect the table contents, and the REG8, REG16 and REG32 macros must
   also reflect the table contents. */

static	REGINFO		xregs[MAXREGNAME] = {
{	"AL",	0x00	},
{	"CL",	0x01	},
{	"DL",	0x02	},
{	"BL",	0x03	},
{	"AH",	0x04	},
{	"CH",	0x05	},
{	"DH",	0x06	},
{	"BH",	0x07	},
{	"AX",	0x08	},
{	"CX",	0x09	},
{	"DX",	0x0a	},
{	"BX",	0x0b	},
{	"SP",	0x0c	},
{	"BP",	0x0d	},
{	"SI",	0x0e	},
{	"DI",	0x0f	},
{	"EAX",	0x10	},
{	"ECX",	0x11	},
{	"EDX",	0x12	},
{	"EBX",	0x13	},
{	"ESP",	0x14	},
{	"EBP",	0x15	},
{	"ESI",	0x16	},
{	"EDI",	0x17	},		/* Last user-level register */
{	"CR0",	0x20	},
{	"CR1",	0x21	},
{	"CR2",	0x22	},
{	"CR3",	0x23	},
{	"CR4",	0x24	},
{	"DR0",	0x30	},
{	"DR1",	0x31	},
{	"DR2",	0x32	},
{	"DR3",	0x33	},
{	"DR4",	0x34	},
{	"DR5",	0x35	},
{	"DR6",	0x36	},
{	"DR7",	0x37	},
{	"TR3",	0x43	},
{	"TR4",	0x44	},
{	"TR5",	0x45	},
{	"TR6",	0x46	},
{	"TR7",	0x47	}
};


/* Operand types found in definition table */

#define	MAXRANDTYPE	32

static	PUCHAR	operands[MAXRANDTYPE] = {
"N",				/*  0 - operand not permitted		*/
"r8",				/*  1 - 8-bit register			*/
"r16",				/*  2 - 16-bit register			*/
"r32",				/*  3 - 32-bit register			*/
"s16",				/*  4 - 16-bit segment register		*/
"cr",				/*  5 - control register (CR0-7)	*/
"dr",				/*  6 - debug register (DR0-7)		*/
"tr",				/*  7 - test register (TR0-7)		*/
"rf",				/*  8 - floating point register		*/
"rt",				/*  9 - floating point top of stack	*/
"imm8",				/* 10 - immediate 8-bit value		*/
"imm16",			/* 11 - immediate 16-bit value		*/
"imm32",			/* 12 - immediate 32-bit value		*/
"moffs8",			/* 13 - 8-bit segment offset		*/
"moffs32",			/* 14 - 32-bit segment offset		*/
"r/m8",				/* 15 - 8-bit register/memory operand	*/
"r/m16",			/* 16 - 16-bit register/memory operand	*/
"r/m32",			/* 17 - 32-bit register/memory operand	*/
"r/m64",			/* 18 - 64-bit register/memory operand	*/
"rel8",				/* 19 - 8-bit relative offset		*/
"rel32",			/* 20 - 32-bit relative offset		*/
"m8",				/* 21 - 8-bit memory operand		*/
"m16",				/* 22 - 16-bit memory operand		*/
"m32",				/* 23 - 32-bit memory operand		*/
"m16:32",			/* 24 - 48-bit far pointer		*/
"m16&32",			/* 25 - 16 and 32 bit data pair		*/
"m64",				/* 26 -	64-bit memory operand		*/
"ptr48",			/* 27 - 48-bit memory operand (pointer)	*/
"m32real",			/* 28 - 32-bit real memory operand	*/
"m64real",			/* 29 - 64-bit real memory operand	*/
"m80real",			/* 30 - 80-bit real memory operand	*/
"m80dec"			/* 31 - 80-bit BCD memory operand	*/
};

/* Other operand types. The values assigned are actually the mod r/m
   values, with the register field set to zero. */

#define	MAXOTHRAND	24

static	RANDINFO	othrands[MAXOTHRAND] = {
					/*	Mod	R/M	*/
{	"i_eax",	0x00	},	/*	00	000	*/
{	"i_ecx",	0x01	},	/*		001	*/
{	"i_edx",	0x02	},	/*		010	*/
{	"i_ebx",	0x03	},	/*		011	*/
{	"sib",		0x04	},	/*		100	*/
{	"disp32",	0x05	},	/*		101	*/
{	"i_esi",	0x06	},	/*		110	*/
{	"i_edi",	0x07	},	/*		111	*/
{	"i_eax+disp8",	0x40	},	/*	01	000	*/
{	"i_ecx+disp8",	0x41	},	/*		001	*/
{	"i_edx+disp8",	0x42	},	/*		010	*/
{	"i_ebx+disp8",	0x43	},	/*		011	*/
{	"i_sib+disp8",	0x44	},	/*		100	*/
{	"i_ebp+disp8",	0x45	},	/*		101	*/
{	"i_esi+disp8",	0x46	},	/*		110	*/
{	"i_edi+disp8",	0x47	},	/*		111	*/
{	"i_eax+disp32",	0x80	},	/*	02	000	*/
{	"i_ecx+disp32",	0x81	},	/*		001	*/
{	"i_edx+disp32",	0x82	},	/*		010	*/
{	"i_ebx+disp32",	0x83	},	/*		011	*/
{	"i_sib+disp32",	0x84	},	/*		100	*/
{	"i_ebp+disp32",	0x85	},	/*		101	*/
{	"i_esi+disp32",	0x86	},	/*		110	*/
{	"i_edi+disp32",	0x87	}	/*		111	*/
};

/* Instruction variant types */

#define	MAXVARIANT	16

static	PUCHAR	variants[MAXVARIANT] = {
"/0",				/*  0 - 0 in 'mod r/m' is part of instr	*/
"/1",				/*  1 - 1 in 'mod r/m' is part of instr	*/
"/2",				/*  2 - 2 in 'mod r/m' is part of instr	*/
"/3",				/*  3 - 3 in 'mod r/m' is part of instr	*/
"/4",				/*  4 - 4 in 'mod r/m' is part of instr	*/
"/5",				/*  5 - 5 in 'mod r/m' is part of instr	*/
"/6",				/*  6 - 6 in 'mod r/m' is part of instr	*/
"/7",				/*  7 - 7 in 'mod r/m' is part of instr	*/
"S",				/*  8 - simple instruction		*/
"/r",				/*  9 - register in 'mod r/m' byte	*/
"+r8",				/* 10 - add 8-bit register to opcode	*/
"+r32",				/* 11 - add 32-bit register to opcode	*/
"+i",				/* 12 - add FP register to opcode	*/
"/c",				/* 13 - control register in 'mod r/m'	*/
"/d",				/* 14 - debug register in 'mod r/m'	*/
"/t"				/* 15 - test register in 'mod r/m'	*/
};


/*
 * Main entry point. Handles files, and basic processing loop.
 *
 */

INT main(INT argc, PUCHAR argv[])
{	INT i;
	PUCHAR ptr;

	progname = argv[0];
	ptr = strrchr(progname, '\\');
	if(ptr != (PUCHAR) NULL) progname = ++ptr;
	for(i = 0; progname[i] != '\0'; i++)
		progname[i] = (UCHAR) tolower(progname[i]);

	if(argc != 5) usage();

	infp = openio(argv[1], "r");
	tabfp = openio(argv[2], "w");
	opfp = openio(argv[3], "w");
	listfp = openio(argv[4], "w");

	/* Main input loop */

	for(;;) {
		if(num >= MAXOP)
			error("too many opcodes");

		readline();

		if(cc[0] == '\0') break;	/* Finished */

		/* Strip comments */

		ptr = strchr(cc, '!');
		if(ptr != (unsigned char *) NULL) {
			*ptr-- = '\0';
			while(strlen(cc) != 0 && *(ptr-1) == ' ')
				*--ptr = '\0';
 		}
		if(cc[0] == '\0') continue;

		/* Read and decompose line */

		sscanf(cc, "%s %s %s %s %s %s %s",
			xmnem, xoperand[0], xoperand[1], xoperand[2],
			xvariant, xjump, xinstr);

		if(do_mnem() == FALSE) continue;

		if(do_operand(1) == FALSE) continue;
		if(do_operand(2) == FALSE) continue;
		if(do_operand(3) == FALSE) continue;

		if(do_variant() == FALSE) continue;

		if(do_jump() == FALSE) continue;

		if(do_instr() == FALSE) continue;

		num++;
	}
	fclose(infp);

	fprintf(stderr, "%s: %d opcodes read\n", progname, num);
	if(errors != 0)
		error("%d errors\nStop.\n", errors);

	/* Generate the tables file */

	if(errors == 0) do_tables(argv[1], argv[2], argv[3]);

	return(errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}


/*
 * Function to process the instruction encoding; returns TRUE for success,
 * FALSE for failure.
 *
 */

static INT do_instr(VOID)
{	INT i, k;
	INT l = (int) strlen(xinstr);
	UINT op = 0;

	switch(l) {
		case 2:
			size[num] = 1;
			break;

		case 4:
			size[num] = 2;
			break;

		case 6:
			size[num] = 3;
			break;

		default:
			warn("invalid opcode length (%d) on line %d", l, line);
			errors++;
			return(FALSE);
	}

	(VOID) strupr(xinstr);
	for(i = 0; i < size[num]*2; i++) {
		k = xinstr[i];
		if(k >= '0' && k <= '9') {
			op = (op << 4) | (k - '0');
		} else {
			if(k >= 'A' && k <= 'F') {
				op = (op << 4) | (k - 'A' + 10);
			} else {
				warn("bad opcode '%s' on line %d\n",
						xinstr, line);
				errors++;
				return(FALSE);
			}
		}
	}
	instr[num] = op;
	return(TRUE);
}


/*
 * Function to process the jump flag; returns TRUE for success,
 * FALSE for failure.
 *
 */

static BOOL do_jump(VOID)
{	(VOID) strupr(xjump);
	if((strlen(xjump) == 1) &&
	   (xjump[0] == 'J' || xjump[0] == 'N')) {
		jump[num] = (unsigned int) ((xjump[0] == 'J') ? 1 : 0);
		return(TRUE);
	} else {
		warn("invalid jump flag '%s' on line %d", xjump, line);
		errors++;
		return(FALSE);
	}
}


/*
 * Function to process mnemonic field; returns TRUE for success,
 * FALSE for failure.
 *
 */

static BOOL do_mnem(VOID)
{	if(strlen(xmnem) > MAXMNEM) {
		warn("mnemonic '%s' too long on line %d", xmnem, line);
		errors++;
		return(FALSE);
	} else {
		(VOID) strcpy(mnem[num], xmnem);
		return(TRUE);
	}
}


/*
 * Function to process operand 'n'; returns TRUE for success,
 * FALSE for failure.
 *
 */

static BOOL do_operand(int n)
{	UINT i;
	PUCHAR r = xoperand[n-1];

	for(i = 0; i < MAXRANDTYPE; i++) {
		if(strcmp(r, operands[i]) == 0) {
			operand[n-1][num] = i;
			return(TRUE);
		}
	}
	warn("invalid operand %d '%s' on line %d", n, r, line);
	errors++;
	return(FALSE);
}


/*
 * Function to process instruction variant; returns TRUE for success,
 * FALSE for failure.
 *
 */

static BOOL do_variant(VOID)
{	UINT i;

	for(i = 0; i < MAXVARIANT; i++) {
		if(strcmp(xvariant, variants[i]) == 0) {
			variant[num] = i;
			return(TRUE);
		}
	}
	warn("invalid variant '%s' on line %d", xvariant, line);
	errors++;
	return(FALSE);
}


/*
 * Routine to output an error message in the style of 'printf', then exit.
 *
 */

static VOID error(PUCHAR s, ...)
{	va_list ap;

	fprintf(stderr, "%s: ", progname);
	va_start(ap, s);
	vfprintf(stderr, s, ap);
	va_end(ap);
	fputc('\n', stderr);

	exit(EXIT_FAILURE);
}


/*
 * Function to generate the subsidiary tables
 *
 */

#if	C
static VOID do_tables(PUCHAR deffile, PUCHAR tabfile, PUCHAR opfile)
{	INT i;
	UINT info1, info2;
	PUCHAR op;
	time_t now;			/* Binary date and time */
	UCHAR s_now[26];		/* String date and time */
	UCHAR temp[50];

	now = time((time_t *) NULL);	/* Get binary date and time */
	strcpy(s_now, ctime(&now));	/* Copy string version */
	s_now[24] = '\0';		/* Truncate to remove newline */

	/* First the #defines */

	fprintf(opfp,
		"/*\n * File: %s\n *\n * Register and instruction"
		" symbol definitions\n *\n",
		opfile);
	fprintf(opfp, " * Generated from file %s on %s\n *\n */\n\n",
		deffile, s_now);

	fputs("/*\n * Macro definitions\n *\n */\n\n", opfp);
	for(i = 0; i < MAXMAC; i++)
		fprintf(opfp, "#define\t%s\n", macros[i]);

	fputs("\n/*\n * Register symbols\n *\n */\n\n", opfp);
	fprintf(opfp, "#define\tMAXREGNAME\t%d\n", MAXREGNAME);
	fprintf(opfp, "#define\tMAXREGS\t\t%d\n\n", MAXREGS);
	for(i = 0; i < MAXREGNAME; i++) {
		fprintf(opfp, "#define\t\t%s\t0x%02x\n",
			xregs[i].name, xregs[i].num);
	}

	fputs("\n/*\n * Opcode symbols\n *\n */\n\n", opfp);
	fprintf(opfp, "#define\tNOOFOPS\t\t%d\n\n", num);
	for(i = 0; i < num; i++) {
		op = mnem[i];
		fprintf(opfp, "#define\tOP_%s\t", op);
		if(strlen(op) < 5) fputc('\t', opfp);
		fprintf(opfp, "%d\n", i);
	}

	fputs("\n/*\n * Pseudo operand symbols\n *\n */\n\n", opfp);
	for(i = 0; i < MAXRANDTYPE; i++) {
		(VOID) strcpy(temp, operands[i]);
		(VOID) strupr(temp);
		translate(temp, "/", "");
		translate(temp, ":", "_");
		translate(temp, "&", "_AND_");
		fprintf(opfp, "#define\tRA_%s\t", temp);
		if(strlen(temp) < 5) fputc('\t', opfp);
		fprintf(opfp, "0x%03x\n", 0x100 + i);
	}

	fputs("\n/*\n * Actual operand symbols\n *\n */\n\n", opfp);
	for(i = 0; i < MAXOTHRAND; i++) {
		(VOID) strcpy(temp, othrands[i].name);
		(VOID) strupr(temp);
		translate(temp, "+", "_");
		fprintf(opfp, "#define\tRA_%s\t", temp);
		if(strlen(temp) < 5) fputc('\t', opfp);
		if(strlen(temp) < 13) fputc('\t', opfp);
		fprintf(opfp, "0x%02x\n", othrands[i].value);
	}

	fputs("\n/*\n * Variant symbols\n *\n */\n\n", opfp);
	for(i = 0; i < MAXVARIANT; i++) {
		(VOID) strcpy(temp, variants[i]);
		(VOID) strupr(temp);
		translate(temp, "/R", "REG");
		translate(temp, "/C", "CREG");
		translate(temp, "/D", "DREG");
		translate(temp, "/T", "TREG");
		translate(temp, "/", "SLASH");
		translate(temp, "+", "PLUS_");
		fprintf(opfp, "#define\tVA_%s\t", temp);
		if(strlen(temp) < 5) fputc('\t', opfp);
		fprintf(opfp, "%d\n", i);
	}

	fprintf(opfp, "\n/*\n * End of file: %s\n *\n */\n", opfile);

	/* Next the register and instruction tables */

	fprintf(tabfp,
		"/*\n * File: %s\n *\n * Register and instruction tables\n *\n",
		tabfile);
	fprintf(tabfp, " * Generated from file %s on %s\n *\n */\n\n",
		deffile, s_now);
	fprintf(tabfp, "#pragma\tinfo(noext)\n\n");

	/* First the user-level registers */

	fputs("/*\n * User-level registers\n *\n */\n\n", tabfp);
	fprintf(tabfp, "const\tunsigned char\t*regs[MAXREGS] = {\n");
	for(i = 0; i < MAXREGS; i++) {
		fprintf(tabfp, "\"%-*s\"%s", 3, xregs[i].name, 
			(i == num - 1) ? "" : ", ");
		if((i+1)%8 == 0) fputc('\n', tabfp);
	}
#if	MAXREGS%8 != 0
	fputc('\n', tabfp);
#endif
	fputs("};\n\n", tabfp);

	/* Next the mnemonics */

	fputs("/*\n * Instruction mnemonics\n *\n */\n\n", tabfp);
	fprintf(tabfp, "unsigned\tint\topcsize = NOOFOPS;\n\n");
	fprintf(tabfp, "const\tunsigned char\t*opc[NOOFOPS] = {\n");

	for(i = 0; i < num; i++) {
		fprintf(tabfp, "\"%-*s\"%s", MAXMNEM, mnem[i], 
			(i == num - 1) ? "" : ", ");
		if((i+1)%4 == 0) fputc('\n', tabfp);
	}		
	if(num%4 != 0) fputc('\n', tabfp);
	fputs("};\n", tabfp);

	/* Now the opcode information tables */

	fputs("\n/*\n * Instruction information tables\n *\n */\n\n",tabfp);
	fputs("const\tunsigned int\topcinfo1[NOOFOPS] = {\n", tabfp);

	for(i = 0; i < num; i++) {
		info1 = (jump[i] << 31)        |
			(variant[i] << 24)     |
			(operand[0][i] << 16)  |
			(operand[1][i] << 8)   |
			 operand[2][i];
		fprintf(tabfp, "0x%08x%s", info1, (i == num - 1) ? "" : ", ");
		if((i+1)%4 == 0) fputc('\n', tabfp);
	}		
	if(num%4 != 0) fputc('\n', tabfp);
	fputs("};\n", tabfp);

	fputs("\nconst\tunsigned int\topcinfo2[NOOFOPS] = {\n", tabfp);

	for(i = 0; i < num; i++) {
		info2 =  (instr[i] << (32-size[i]*8)) | size[i];
		fprintf(tabfp, "0x%08x%s", info2, (i == num - 1) ? "" : ", ");
		if((i+1)%4 == 0) fputc('\n', tabfp);
	}		
	if(num%4 != 0) fputc('\n', tabfp);
	fputs("};\n", tabfp);
	fprintf(tabfp, "\n/*\n * End of file: %s\n *\n */\n", tabfile);

	/* Now the listing file */

	fprintf(listfp, "\nOpcode listing generated from file %s on %s\n\n",
		deffile, s_now);
	fprintf(listfp, "\t%d opcodes defined\n\n\n", num);
	fputs("Mnemonic  Idx\tOperand\tOperand\tOperand\tVariant\t"
		"  Jump\t   Info1      Info2\n",
		listfp);
	fputs("\t\t   1\t   2\t   3\t\t  flag\n\n", listfp);

	for(i = 0; i < num; i++) {
		info1 = (jump[i] << 31)        |
			(variant[i] << 24)     |
			(operand[0][i] << 16)  |
			(operand[1][i] << 8)   |
			 operand[2][i];
		info2 = (instr[i] << (32-size[i]*8)) | size[i];
		fprintf(listfp,
			"%8s  %3d\t%s\t%s\t%s\t",
			mnem[i],
			i,
			operands[operand[0][i]],
			operands[operand[1][i]],
			operands[operand[2][i]]);
		fprintf(listfp,
			"  %s\t   %c\t  %08x   %08x\n",
			variants[variant[i]],
			jump[i] ? 'J' : 'N',
			info1, info2);
	}		
}
#endif


/*
 * Function to generate the subsidiary tables
 *
 */

#if	IMP
static VOID do_tables(PUCHAR deffile)
{	INT i;

	/* First the opcode table */

	fprintf(tabfp, "%%constantinteger no of ops = %d\n", num-1);
	fprintf(tabfp, "%%constantstring(%d)array tsname(0:no of ops) = %%c\n", 
		MAXMNEM);

	for(i = 0; i < num; i++) {
		fprintf(tabfp, "\"%-*s\"%c", MAXMNEM, opcode[i], 
			(i == num - 1) ? ';' : ', ');
		if((i+1)%4 == 0) fputc('\n', tabfp);
	}		
	if(num%4 != 0) fputc('\n', tabfp);

	/* Now the opcode information table */

	fputs("!\n%constantintegerarray opcinfo(0:no of sns) = %c\n", tabfp);

	for(i = 0; i < num; i++) {
		fprintf(tabfp, "x'%08x'%c", opcinfo[i], (i == num - 1) ? ';' : ', ');
		if((i+1)%4 == 0) fputc('\n', tabfp);
	}		
	if(num%4 != 0) fputc('\n', tabfp);
}
#endif


/*
 * Function to open a file in a specified mode
 * Does not return if there is a failure, but outputs an error
 * message and exits
 *
 */

static FILE *openio(PUCHAR file, PUCHAR mode)
{	FILE *fp;

	fp = fopen(file, mode);
	if(fp == (FILE *) NULL) {
		fprintf(stderr, "%s: cannot open '%s'\n", progname, file);
		exit(EXIT_FAILURE);
	}
	return(fp);
}


/*
 * Routine to read a line from the input file into 'cc'.
 * Blank lines are ignored.
 *
 * On exit, 'cc' contains the string, null terminated.
 * 'line' has been updated.
 *
 */

static VOID readline(VOID)
{	INT i = 0;
	INT ch;

	do {
		for(;;) {
			ch = fgetc(infp);
			if(ch == EOF) break;
			if(ch == '\n') break;

			cc[i++] = (UCHAR) ch;
		}
		cc[i] = '\0';
	} while((cc[0] == '\0') && (ch == '\n'));
	line++;
	if(ch == EOF) cc[0] = '\0';
}


/*
 * Routine to replace all occurrences of string 'old' with string 'new'
 * in string 's'; note that 's' must be large enough to accommodate
 * any increase in length.
 *
 */

static VOID translate(PUCHAR s, PUCHAR old, PUCHAR new)
{	PUCHAR p;
	UCHAR temp[50];

	for(;;) {
		p = strstr(s, old);		/* Find 'old' in 's' */
		if(p == NULL) break;		/* Not found */
		*p = '\0';			/* Truncate 's' here */
		(VOID) strcpy(temp, s);		/* Copy head */
		(VOID) strcat(temp, new);	/* Insert replacement */
		p += strlen(old);		/* Move past substring */
		(VOID) strcat(temp, p);		/* Attach old tail */
		(VOID) strcpy(s, temp);		/* Move back to original */
	};
}


/*
 * Routine to output brief usage information, then exit.
 *
 */

static VOID usage(VOID)
{	fprintf(stderr, "Usage: %s input asmtable opcodes listing\n", progname);
	exit(EXIT_FAILURE);
}


/*
 * Routine to output a warning message in the style of 'printf'.
 *
 */

static VOID warn(PUCHAR s, ...)
{	va_list ap;

	fprintf(stderr, "%s: ", progname);
	va_start(ap, s);
	vfprintf(stderr, s, ap);
	va_end(ap);
	fputc('\n', stderr);
}

/*
 * End of file: genop.c
 *
 */
