Metropoli BBS
VIEWER: mktables.c MODE: TEXT (ASCII)
/*
 *	This program reads in the various data files, performs some checks,
 *	and spits out the tables needed for assembly and disassembly.
 *
 *	The input files are:
 *		instr.set	Instruction set
 *		instr.key	Translation from one- or two-character keys to
 *				operand list types
 *		instr.ord	Ordering relations to enforce on the operands
 *
 *	The output tables are merged into the debug source file debug.a86.
 */

#ifndef	DOS
#ifdef	__MSDOS__
#define	DOS	1
#else
#define	DOS	0
#endif
#endif

#include <stdlib.h>
#if	! DOS
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#if	DOS
#define	CRLF	"\n"
#define	bzero(a, b)	memset(a, 0, b)
#else
#define	CRLF	"\r\n"
#define	cdecl
#endif

#define	MARKLINE1	";-@@-@@-@@-Do not edit these tables!  " \
	"They have been automatically generated." CRLF
#define	MARKLINE2	";-@@-@@-@@-End of auto-generated tables.  " \
	"You may edit below this point." CRLF

#define	MAX_OL_TYPES	80
#define	MAX_N_ORDS	30
#define	LINELEN		132
#define	MAX_ASM_TAB	2048
#define	MAX_MNRECS	400
#define	MAX_SAVED_MNEMS	10
#define	MAX_SLASH_ENTRIES 20
#define	MAX_STAR_ENTRIES 15
#define	MAX_LOCKTAB_ENTRIES 50
#define	MAX_AGROUP_ENTRIES 14
#define	MSHIFT		12	/* number of bits below machine type */

typedef	char	Boolean;
#define	True	1
#define	False	0

#define	NUMBER(x)	(sizeof(x) / sizeof(*x))

char		line[LINELEN];
const char	*filename;
int		lineno;

int	n_keys		= 0;
struct keytab {
	short	key;
	short	value;
	short	width;
};

int	n_ol_types	= 0;
struct keytab	olkeydict[MAX_OL_TYPES];
char		*olnames[MAX_OL_TYPES];
int		oloffset[MAX_OL_TYPES];

int	n_ords		= 0;
struct keytab	*keyord1[MAX_N_ORDS];
struct keytab	*keyord2[MAX_N_ORDS];
Boolean		ordsmall[MAX_OL_TYPES];

/*
 *	Equates for the assembler table.
 *	These should be the same as in debug.asm.
 */

#define	ASM_END		0xff
#define	ASM_DB		0xfe
#define	ASM_DW		0xfd
#define	ASM_DD		0xfc
#define	ASM_ORG		0xfb
#define	ASM_WAIT 	0xfa
#define	ASM_D32		0xf9
#define	ASM_AAX		0xf8
#define	ASM_SEG		0xf7
#define	ASM_LOCKREP	0xf6
#define	ASM_LOCKABLE	0xf5
#define	ASM_MACH6	0xf4
#define	ASM_MACH0	0xee

int	n_asm_tab	= 0;
unsigned char	asmtab[MAX_ASM_TAB];

struct mnrec {
	struct mnrec	*next;
	char		*string;
	short		len;
	short		offset;
	short		asmoffset;
};

int		num_mnrecs;
struct mnrec	mnlist[MAX_MNRECS];
struct mnrec	*mnhead;

int	n_saved_mnems		= 0;
int	saved_mnem[MAX_SAVED_MNEMS];

int	n_slash_entries;
int	slashtab_seq[MAX_SLASH_ENTRIES];
int	slashtab_mn[MAX_SLASH_ENTRIES];

int	n_star_entries;
int	startab_seq[MAX_STAR_ENTRIES];
int	startab_mn[MAX_STAR_ENTRIES];

int	n_locktab;
int	locktab[MAX_LOCKTAB_ENTRIES];

int	n_agroups;
int	agroup_i[MAX_AGROUP_ENTRIES];
int	agroup_inf[MAX_AGROUP_ENTRIES];

volatile void
fail(const char *message, ...)
{
	va_list	args;

	va_start(args, message);
	vfprintf(stderr, message, args);
	va_end(args);
	putc('\n', stderr);
	exit(1);
}

FILE *
openread(const char *path)
{
	FILE	*f;

	f = fopen(path, "r");
	if (f == NULL) {
	    perror(path);
	    exit(1);
	}
	filename = path;
	lineno = 0;
	return f;
}

volatile void
linenofail(const char *message, ...)
{
	va_list args;

	fprintf(stderr, "Line %d of `%s':  ", lineno, filename);
	va_start(args, message);
	vfprintf(stderr, message, args);
	va_end(args);
	putc('\n', stderr);
	exit(1);
}

void *
xmalloc(unsigned int len, const char *why)
{
	void	*ptr	= malloc(len);

	if (ptr == NULL) fail("Cannot allocate %u bytes for %s", len, why);
	return ptr;
}

Boolean
getline(FILE *ff)
{
	int	n;

	for (;;) {
	    if (fgets(line, LINELEN, ff) == NULL) return False;
	    ++lineno;
	    if (line[0] == '#') continue;
	    n = strlen(line) - 1;
	    if (n < 0 || line[n] != '\n')
		linenofail("too long.");
	    if (n > 0 && line[n-1] == '\r') --n;
	    if (n == 0) continue;
	    line[n] = '\0';
	    return True;
	}
}

short
getkey(char **pp)
{
	short	key;
	char	*p	= *pp;

	if (*p == ' ' || *p == '\t' || *p == ';' || *p == '\0')
	    linenofail("key expected");
	key = *p++;
	if (*p != ' ' && *p != '\t' && *p != ';' && *p != '\0') {
	    key = (key << 8) | *p++;
	    if (*p != ' ' && *p != '\t' && *p != ';' && *p != '\0')
		linenofail("key too long");
	}
	*pp = p;
	return key;
}

/*
 *	Mark the given key pointer as small, as well as anything smaller than
 *	it (according to instr.ord).
 */

void
marksmall(struct keytab *kp)
{
	int	i;

	ordsmall[kp - olkeydict] = True;
	for (i = 0; i < n_ords; ++i)
	    if (keyord2[i] == kp)
		marksmall(keyord1[i]);
}

/*
 *	Add a byte to the assembler table (asmtab).
 *	The format of this table is described in a long comment in debug.asm,
 *	somewhere within the mini-assembler.
 */

void
add_to_asmtab(unsigned char byte)
{
	if (n_asm_tab >= MAX_ASM_TAB)
	    linenofail("Assembler table overflow.");
	asmtab[n_asm_tab++] = byte;
}


unsigned char
getmachine(char **pp)
{
	char		*p	= *pp;
	unsigned char	value;

	if (*p != ';') return 0;
	++p;
	if (*p < '0' || *p > '6')
	    linenofail("bad machine type");
	value = *p++ - '0';
	add_to_asmtab(ASM_MACH0 + value);
	*pp = p;
	return value;
}

struct keytab *
lookupkey(short key)
{
	struct keytab *kp;

	for (kp = olkeydict; kp < olkeydict + NUMBER(olkeydict); ++kp)
	    if (key == kp->key) return kp;
	linenofail("can't find key");
}

char *
skipwhite(char *p)
{
	while (*p == ' ' || *p == '\t') ++p;
	return p;
}

/*
 *	Data and setup stuff for the disassembler processing.
 */

/*	Data on coprocessor groups */

unsigned int	fpgrouptab[]	= {0xd9e8, 0xd9f0, 0xd9f8};

#define	NGROUPS		9

#define	GROUP(i)	(256 + 8 * ((i) - 1))
#define	COPR(i)		(256 + 8 * NGROUPS + 16 * (i))
#define	FPGROUP(i)	(256 + 8 * NGROUPS + 16 * 8 + 8 * (i))
#define	SPARSE_BASE	(256 + 8 * NGROUPS + 16 * 8 \
			    + 8 * NUMBER(fpgrouptab))

/* #define OPILLEGAL	0 */
#define	OPTWOBYTE	2
#define	OPGROUP		4
#define	OPCOPR		6
#define	OPFPGROUP	8
#define	OPPREFIX	10
#define	OPSIMPLE	12
#define	OPTYPES		12	/* op types start here (includes simple ops) */

#define	PRESEG		1	/* these should be the same as in debug.asm */
#define	PREREP		2
#define	PRELOCK		4
#define	PRE32D		8
#define	PRE32A		0x10

/*
 *	For sparsely filled parts of the opcode map, we have counterparts
 *	to the above, which are compressed in a simple way.
 */

/*	Sparse coprocessor groups */

unsigned int	sp_fpgrouptab[]	= {0xd9d0, 0xd9e0, 0xdae8, 0xdbe0, 
				   0xded8, 0xdfe0};

#define	NSGROUPS	5

#define	SGROUP(i)	(SPARSE_BASE + 256 + 8 * ((i) - 1))
#define	SFPGROUP(i)	(SPARSE_BASE + 256 + 8 * NSGROUPS + 8 * (i))
#define	NOPS		(SPARSE_BASE + 256 + 8 * NSGROUPS \
			+ 8 * NUMBER(sp_fpgrouptab))

int		optype[NOPS];
int		opinfo[NOPS];
unsigned char	opmach[NOPS];

/*
 *	Here are the tables for the main processor groups.
 */

struct {
	int	seq;	/* sequence number of the group */
	int	info;	/* which group number it is */
}
	grouptab[]	= {
		{0x80, GROUP(1)},	/* Intel group 1 */
		{0x81, GROUP(1)},
		{0x83, GROUP(2)},
		{0xd0, GROUP(3)},	/* Intel group 2 */
		{0xd1, GROUP(3)},
		{0xd2, GROUP(4)},
		{0xd3, GROUP(4)},
		{0xc0, GROUP(5)},	/* Intel group 2a */
		{0xc1, GROUP(5)},
		{0xf6, GROUP(6)},	/* Intel group 3 */
		{0xf7, GROUP(6)},
		{0xff, GROUP(7)},	/* Intel group 5 */
		{SPARSE_BASE + 0x00, GROUP(8)},		/* Intel group 6 */
		{SPARSE_BASE + 0x01, GROUP(9)}};	/* Intel group 7 */

/* #define	NGROUPS	9 (this was done above) */

struct {	/* sparse groups */
	int	seq;	/* sequence number of the group */
	int	info;	/* which group number it is */
}
	sp_grouptab[]	= {
		{0xfe, SGROUP(1)},		/* Intel group 4 */
		{SPARSE_BASE+0xba, SGROUP(2)},	/* Intel group 8 */
		{SPARSE_BASE+0xc7, SGROUP(3)},	/* Intel group 9 */
		{0x8f, SGROUP(4)},		/* Not an Intel group */
		{0xc6, SGROUP(5)},		/* Not an Intel group */
		{0xc7, SGROUP(5)}};

/* #define	NSGROUPS	5 (this was done above) */

/*
 *	Creates an entry in the disassembler lookup table
 */

void
entertable(int i, int type, int info)
{
	if (optype[i] != 0)
	    linenofail("Duplicate information for index %d", i);
	optype[i] = type;
	opinfo[i] = info;
}

/*
 *	Get a hex nybble from the input line or fail.
 */

int
getnybble(char c)
{
	if (c >= '0' && c <= '9') return c - '0';
	if (c >= 'a' && c <= 'f') return c - 'a' + 10;
	linenofail("Hex digit expected instead of `%c'", c);
}

/*
 *	Get a hex byte from the input line and update the pointer accordingly.
 */

int
getbyte(char **pp)
{
	char	*p = *pp;
	int	answer;

	answer = getnybble(*p++);
	answer = (answer << 4) | getnybble(*p++);
	*pp = p;
	return answer;
}

/*
 *	Get a `/r' descriptor from the input line and update the pointer
 *	accordingly.
 */

int
getslash(char **pp)
{
	char	*p = *pp;
	int	answer;

	if (*p != '/') linenofail("`/' expected");
	++p;
	if (*p < '0' || *p > '7') linenofail("Octal digit expected");
	answer = *p - '0';
	++p;
	*pp = p;
	return answer;
}

int
entermn(char *str, char *str_end)
{
	char	*p;

	if (num_mnrecs >= MAX_MNRECS)
	    linenofail("Too many mnemonics");

	if (*str == '+') {
	    if (n_saved_mnems >= MAX_SAVED_MNEMS)
		linenofail("Too many mnemonics to save");
	    saved_mnem[n_saved_mnems++] = num_mnrecs;
	    ++str;
	}

	p = xmalloc(str_end - str + 1, "mnemonic name");
	mnlist[num_mnrecs].string = p;
	mnlist[num_mnrecs].len = str_end - str;
	while (str < str_end) *p++ = toupper(*str++);
	*p = '\0';
	mnlist[num_mnrecs].asmoffset = n_asm_tab;
	return num_mnrecs++;
}

/*
 *	Merge sort the indicated range of mnemonic records.
 */

struct mnrec *
mn_sort(struct mnrec *start, int len)
{
	struct mnrec	*p1, *p2, *answer;
	struct mnrec	**headpp;
	int		i;

	i = len / 2;
	if (i == 0)
	    return start;

	p1 = mn_sort(start, i);
	p2 = mn_sort(start + i, len - i);
	headpp = &answer;
	for (;;)
	    if (strcmp(p1->string, p2->string) < 0) {
		*headpp = p1;
		headpp = &p1->next;
		p1 = *headpp;
		if (p1 == NULL) {
		    *headpp = p2;
		    break;
		}
	    }
	    else {
		*headpp = p2;
		headpp = &p2->next;
		p2 = *headpp;
		if (p2 == NULL) {
		    *headpp = p1;
		    break;
		}
	    }

	return answer;
}

/*
 *	This reads the main file, "instr.set".
 */

void
read_is(FILE *f1)
{
	int	i;

	entertable(0x0f, OPTWOBYTE, SPARSE_BASE);
	entertable(0x26, OPPREFIX, PRESEG | (0 << 8));	/* seg es */
	entertable(0x2e, OPPREFIX, PRESEG | (1 << 8));	/* seg cs */
	entertable(0x36, OPPREFIX, PRESEG | (2 << 8));	/* seg ss */
	entertable(0x3e, OPPREFIX, PRESEG | (3 << 8));	/* seg ds */
	entertable(0x64, OPPREFIX, PRESEG | (4 << 8));	/* seg fs */
	entertable(0x65, OPPREFIX, PRESEG | (5 << 8));	/* seg gs */
	entertable(0xf2, OPPREFIX, PREREP);		/* other prefixes */
	entertable(0xf3, OPPREFIX, PREREP);
	entertable(0xf0, OPPREFIX, PRELOCK);
	entertable(0x66, OPPREFIX, PRE32D);
	entertable(0x67, OPPREFIX, PRE32A);
	opmach[0x64] = opmach[0x65] = opmach[0x66] = opmach[0x67] = 3;
	
	for (i = 0; i < NUMBER(grouptab); ++i)
	    entertable(grouptab[i].seq, OPGROUP, grouptab[i].info);
	for (i = 0; i < NUMBER(sp_grouptab); ++i)
	    entertable(sp_grouptab[i].seq, OPGROUP, sp_grouptab[i].info);
	for (i = 0; i < 8; ++i)
	    entertable(0xd8 + i, OPCOPR, COPR(i));
	for (i = 0; i < NUMBER(fpgrouptab); ++i) {
	    unsigned int j = fpgrouptab[i];
	    unsigned int k = (j >> 8) - 0xd8;

	    if (k > 8 || (j & 0xff) < 0xc0)
		fail("Bad value for fpgrouptab[%d]", i);
	    entertable(COPR(k) + 8 + (((j & 0xff) - 0xc0) >> 3),
		OPFPGROUP, FPGROUP(i));
	}
	for (i = 0; i < NUMBER(sp_fpgrouptab); ++i) {
	    unsigned int j = sp_fpgrouptab[i];
	    unsigned int k = (j >> 8) - 0xd8;

	    if (k > 8 || (j & 0xff) < 0xc0)
		fail("Bad value for sp_fpgrouptab[%d]", i);
	    entertable(COPR(k) + 8 + (((j & 0xff) - 0xc0) >> 3),
		OPFPGROUP, SFPGROUP(i));
	}
	while (getline(f1)) {	/* loop over lines in the file */
	    int mnem;
	    int mn_alt;
	    char *p, *p0, *pslash, *pstar;
	    Boolean asm_only_line;
	    unsigned char atab_addendum;

	    asm_only_line = False;
	    p0 = line;
	    if (line[0] == '_') {
		asm_only_line = True;
		++p0;
	    }
	    atab_addendum = '\0';
	    if (*p0 == '^') {
		static	const	unsigned char	uptab[]	=
					{ASM_AAX, ASM_DB, ASM_DW,
					 ASM_DD, ASM_ORG, ASM_D32};

		++p0;
		atab_addendum = uptab[*p0++ - '0'];
	    }
	    p = strchr(p0, ' ');
	    if (p == NULL) p = p0 + strlen(p0);
	    pslash = memchr(p0, '/', p - p0);
	    if (pslash != NULL) {
		mnem = entermn(p0, pslash);
		++mnlist[mnem].asmoffset;	/* this one isn't 32 bit */
		++pslash;
		mn_alt = entermn(pslash, p);
		add_to_asmtab(ASM_D32);
	    }
	    else {
		pstar = memchr(p0, '*', p - p0);
		if (pstar != NULL) {
		    mn_alt = entermn(p0, pstar);	/* note the reversal */
		    ++pstar;
		    add_to_asmtab(ASM_WAIT);
		    mnem = entermn(pstar, p);
		}
		else
		    mnem = entermn(p0, p);
	    }
	    if (atab_addendum != '\0') add_to_asmtab(atab_addendum);

	    atab_addendum = ASM_END;
	    bzero(ordsmall, n_keys * sizeof(Boolean));
	    while (*p == ' ') {		/* loop over instruction variants */
		Boolean		lockable;
		Boolean		asm_only;
		Boolean		dis_only;
		unsigned char	machine;
		unsigned long	atab_inf;
		unsigned short	atab_key;
		unsigned char	atab_xtra	= 0;

		while (*p == ' ') ++p;
		asm_only = asm_only_line;
		dis_only = False;
		if (*p == '_') {	/* if assembler only */
		    ++p;
		    asm_only = True;
		}
		else if (*p == 'D') {	/* if disassembler only */
		    ++p;
		    dis_only = True;
		}
		lockable = False;
		if (*p == 'L') {
		    ++p;
		    lockable = True;
		    add_to_asmtab(ASM_LOCKABLE);
		}
		atab_inf = i = getbyte(&p);
		if (i == 0x0f) {
		    i = getbyte(&p);
		    atab_inf = 256 + i;
		    i += SPARSE_BASE;
		}
		if (optype[i] == OPGROUP) {
		    int j = getslash(&p);
		    int k;

		    for (k = 0;; ++k) {
			if (k >= n_agroups) {
			    if (++n_agroups > MAX_AGROUP_ENTRIES)
				linenofail("Too many agroup entries");
			    agroup_i[k] = i;
			    agroup_inf[k] = atab_inf;
			    break;
			}
			if (agroup_i[k] == i)
			    break;
		    }
		    atab_inf = 256 + 256 + 64 + 8 * k + j;
		    i = opinfo[i] + j;
		}
		if (optype[i] == OPCOPR) {
		    if (*p == '/') {
			int	j	= getslash(&p);

			atab_inf = 256 + 256 + j * 8 + (i - 0xd8);
			i = opinfo[i] + j;
		    }
		    else {
			atab_xtra = getbyte(&p);
			if (atab_xtra < 0xc0)
			    linenofail("Bad second escape byte");
			i = opinfo[i] + 8 + ((atab_xtra - 0xc0) >> 3);
			if (optype[i] == OPFPGROUP)
			    i = opinfo[i] + (atab_xtra & 7);
		    }
		}
		switch (*p++) {
		    case '.':
			machine = getmachine(&p);
			if (!asm_only) {
			    entertable(i, OPSIMPLE, mnem);
			    opmach[i] = machine;
			}
			atab_key = 0;
			/* none of these are lockable */
			break;
		    case '*':	/* lock or rep... prefix */
			add_to_asmtab(ASM_LOCKREP);
			add_to_asmtab(atab_inf);	/* special case */
			atab_addendum = '\0';
			break;
		    case '&':	/* segment prefix */
			add_to_asmtab(ASM_SEG);
			add_to_asmtab(atab_inf);	/* special case */
			atab_addendum = '\0';
			break;
		    case ':': {
			    struct keytab *kp = lookupkey(getkey(&p));
			    int width = kp->width;
			    int j;

			    machine = getmachine(&p);
			    if (dis_only)
				atab_addendum = '\0';
			    else {
				if (ordsmall[kp - olkeydict])
				    linenofail("Variants out of order.");
				marksmall(kp);
			    }
			    atab_key = kp->value + 1;
			    if ((i >= 256 && i < SPARSE_BASE)
				    || i >= SPARSE_BASE + 256) {
				if (width > 2)
				    linenofail("width failure");
				width = 1;
			    }
			    if (i & (width - 1))
				linenofail("width alignment failure");
			    if (!asm_only)
				for (j = (i == 0x90); j < width; ++j) {
				    /*    ^^^^^^^^^  kludge for NOP instr. */
				    entertable(i|j, oloffset[kp->value], mnem);
				    opmach[i | j] = machine;
				    if (lockable) {
					if (n_locktab >= MAX_LOCKTAB_ENTRIES)
					    linenofail("Too many lockable "
					      "instructions");
					locktab[n_locktab] = i | j;
					++n_locktab;
				    }
				}
			}
			break;
		    default:
			linenofail("Syntax error.");
		}
		if (atab_addendum != '\0') {
		    atab_inf = atab_inf * (unsigned short) (n_ol_types + 1)
		      + atab_key;
		    add_to_asmtab(atab_inf >> 8);
		    if ((atab_inf >> 8) >= ASM_MACH0)
			fail("Assembler table is too busy");
		    add_to_asmtab(atab_inf);
		    if (atab_xtra != 0)
			add_to_asmtab(atab_xtra);
		}
		if (pslash != NULL) {
		    if (n_slash_entries >= MAX_SLASH_ENTRIES)
			linenofail("Too many slash entries");
		    slashtab_seq[n_slash_entries] = i;
		    slashtab_mn[n_slash_entries] = mn_alt;
		    ++n_slash_entries;
		}
		else if (pstar != NULL) {
		    if (n_star_entries >= MAX_STAR_ENTRIES)
			linenofail("Too many star entries");
		    startab_seq[n_star_entries] = i;
		    startab_mn[n_star_entries] = mn_alt;
		    ++n_star_entries;
		}
	    }
	    if (*p != '\0')
		linenofail("Syntax error.");
	    if (atab_addendum != '\0')
		add_to_asmtab(atab_addendum);	/* ASM_END, if applicable */
	}
}

/*
 *	Print everything onto the file.
 */

struct inforec {	/* strings to put into comment fields */
	int	seqno;
	char	*string;
}
	tblcomments[] = {
		{0, "main opcode part"},
		{GROUP(1), "Intel group 1"},
		{GROUP(3), "Intel group 2"},
		{GROUP(5), "Intel group 2a"},
		{GROUP(6), "Intel group 3"},
		{GROUP(7), "Intel group 5"},
		{GROUP(8), "Intel group 6"},
		{GROUP(9), "Intel group 7"},
		{COPR(0), "Coprocessor d8"},
		{COPR(1), "Coprocessor d9"},
		{COPR(2), "Coprocessor da"},
		{COPR(3), "Coprocessor db"},
		{COPR(4), "Coprocessor dc"},
		{COPR(5), "Coprocessor dd"},
		{COPR(6), "Coprocessor de"},
		{COPR(7), "Coprocessor df"},
		{FPGROUP(0), "Coprocessor groups"},
		{-1, NULL}};

void
put_dw(FILE *f2, const char *label, int *datap, int n)
{
	const char	*initstr;
	int		i;

	fputs(label,f2);
	while (n > 0) {
	    initstr = "\tdw\t";
	    for (i = (n <= 8 ? n : 8); i > 0; --i) {
		fputs(initstr, f2);
		initstr = ",";
		fprintf(f2, "%5d", *datap++);
	    }
	    fputs(CRLF, f2);
	    n -= 8;
	}
}

void
dumptables(FILE *f2)
{
	int		offset;
	struct mnrec	*mnp;
	int		colsleft;
	char		*auxstr;
	struct inforec	*tblptr;
	int		i;
	int		j;
	int		k;

	/*
	 * Dump out asmtab.
	 */

	auxstr = CRLF ";\tMain data table for the assembler." CRLF CRLF
	  "asmtab\tdb\t";
	colsleft = 16;
	for (i = 0; i < n_asm_tab; ++i) {
	    fprintf(f2, "%s%d", auxstr, asmtab[i]);
	    auxstr = ",";
	    if (--colsleft <= 0) {
		auxstr = CRLF "\tdb\t";
		colsleft = 16;
	    }
	}

	/*
	 * Dump out agroup_inf.
	 */

	auxstr = CRLF CRLF ";\tData on groups (for the assembler)." CRLF CRLF
	  "agroups\tdw\t";
	for (i = 0; i < n_agroups; ++i) {
	    fprintf(f2, "%s%d", auxstr, agroup_inf[i]);
	    auxstr = ",";
	}

	/*
	 * Sort the mnemonics alphabetically, compute their offsets,
	 * and print out the table.
	 */

	if (num_mnrecs == 0)
	    fail("No assembler mnemonics!");
	mnhead = mn_sort(mnlist, num_mnrecs);
	offset = 0;
	auxstr = CRLF CRLF ";\tThis is the list of assembler mnemonics." CRLF
	  CRLF "mnlist\tdb\t";
	colsleft = 80 - 15;
	for (mnp = mnhead; mnp != NULL; mnp = mnp->next) {
	    mnp->offset = offset + 2;
	    offset += mnp->len + 3;
	    if (colsleft < mnp->len + 13) {
		auxstr = CRLF "\tdb\t";
		colsleft = 80 - 15;
	    }
	    colsleft -= mnp->len + 13;
	    fprintf(f2, "%s%d,%d,\"%s\",0", auxstr, mnp->asmoffset / 255 + 1,
		mnp->asmoffset % 255 + 1, mnp->string);
	    auxstr = ",";
	}
	fputs(CRLF "end_mnlist db" CRLF, f2);
	if (offset >= (1 << MSHIFT)) {
	    fprintf(stderr, "%d bytes of mnemonics.  That's too many.\n",
		offset);
	    exit(1);
	}

	/*
	 * Print out mnemonics we want to refer to.
	 */

	fputs(CRLF ";\tThese are equates to refer to the above mnemonics."
		CRLF CRLF, f2);
	for (i = 0; i < n_saved_mnems; ++i) {
	    mnp = mnlist + saved_mnem[i];
	    fprintf(f2, "MNEM_%s\tEQU\t%d" CRLF, mnp->string, mnp->offset);
	}

	/*
	 * Print out ASMMOD and the opindex array.
	 */

	fprintf(f2, CRLF ";\tNumber of entries in the following array." CRLF
	  CRLF "ASMMOD\tEQU\t%d" CRLF, n_ol_types + 1);

	auxstr = CRLF ";\tThis is an array of indices into the oplists array "
	  "(below)." CRLF
	  ";\tIt is used by the assembler to save space." CRLF CRLF
	  "opindex\tdb\t0,";
	colsleft = 16;
	for (i = 0; i < n_ol_types; ++i) {
	    fprintf(f2, "%s%d", auxstr, oloffset[i] - OPTYPES);
	    auxstr = ",";
	    if (--colsleft <= 0) {
		auxstr = CRLF "\tdb\t";
		colsleft = 16;
	    }
	}

	/*
	 * Print out oplists[]
	 */

	fputs(CRLF CRLF ";\tThese are the lists of operands "
	    "for the various instruction types." CRLF CRLF
	    "oplists\tdb\t0\t;simple instruction" CRLF, f2);
	for (i = 0; i < n_ol_types; ++i)
	    fprintf(f2, "\tdb\t%s, 0" CRLF, olnames[i]);
	fprintf(f2, CRLF "OPTYPES_BASE\tEQU\t%d" CRLF, OPTYPES);
	fprintf(f2, CRLF "OPLIST_Z\tEQU\t%d" CRLF,
	    oloffset[lookupkey('z')->value]);
	fprintf(f2, "OPLIST_ES\tEQU\t%d" CRLF,
	    oloffset[lookupkey(('E' << 8) | 'S')->value]);

	/*
	 * Print out optype[]
	 */

	auxstr = CRLF ";\tHere is the compressed table of the opcode types."
	    CRLF CRLF "optypes\tdb\t";
	tblptr = tblcomments;

	for (i = 0; i < SPARSE_BASE; i += 8) {
	    for (j = 0; j < 8; ++j) {
		fputs(auxstr, f2);
		fprintf(f2, "%3d", optype[i + j]);
		auxstr = ",";
	    }
	    fprintf(f2, "\t; %02x - %02x", i, i + 7);
	    if (i == tblptr->seqno)
		fprintf(f2, " (%s)", (tblptr++)->string);
	    auxstr = CRLF "\tdb\t";
	}
	auxstr = CRLF ";\tThe rest of these are squeezed." CRLF "\tdb\t 0,";
	colsleft = 7;
	for (i = SPARSE_BASE; i < NOPS; ++i)
	    if ((j = optype[i]) != 0) {
		if (--colsleft < 0) {
		    colsleft = 7;
		    auxstr = CRLF "\tdb\t";
		}
		fputs(auxstr, f2);
		fprintf(f2, "%3d", j);
		auxstr = ",";
	    }
	fputs(CRLF, f2);

	/*
	 * Print out opinfo[]
	 */

	fputs(CRLF ";\tAnd here is the compressed table of additional "
	    "information." CRLF CRLF "opinfo", f2);

	for (i = 0; i < SPARSE_BASE; i += 8) {
	    auxstr = "\tdw\t";
	    for (j = 0; j < 8; ++j) {
		fputs(auxstr, f2);
		k = opinfo[i + j];
		if (optype[i + j] >= OPTYPES)
		    k = mnlist[k].offset;
		fprintf(f2, "0%04xh", k | (opmach[i + j] << MSHIFT));
		auxstr = ",";
	    }
	    fprintf(f2, "\t; %02x" CRLF, i);
	}
	auxstr = ";\tThe rest of these are squeezed." CRLF "\tdw\t     0,";
	colsleft = 7;
	for (i = SPARSE_BASE; i < NOPS; ++i)
	    if ((j = optype[i]) != 0) {
		if (--colsleft < 0) {
		    colsleft = 7;
		    auxstr = CRLF "\tdw\t";
		}
		fputs(auxstr, f2);
		k = opinfo[i];
		if (j >= OPTYPES) k = mnlist[k].offset;
		fprintf(f2, "0%04xh", k | (opmach[i] << MSHIFT));
		auxstr = ",";
	    }
	fputs(CRLF, f2);

	/*
	 * Print out sqztab
	 */

	fputs(CRLF ";\tThis table converts unsqueezed numbers to squeezed."
	    CRLF CRLF "sqztab", f2);

	k = 0;
	for (i = SPARSE_BASE; i < NOPS; i += 8) {
	    auxstr = "\tdb\t";
	    for (j = 0; j < 8; ++j) {
		fprintf(f2, "%s%3d", auxstr, optype[i + j] == 0 ? 0 : ++k);
		auxstr = ",";
	    }
	    fputs(CRLF, f2);
	}

	/*
	 * Print out the cleanup tables.
	 */

	fputs(CRLF ";\tThis is the table of mnemonics that change in the "
		"presence of a WAIT" CRLF ";\tinstruction." CRLF CRLF, f2);
	put_dw(f2, "wtab1", startab_seq, n_star_entries);
	for (i = 0; i < n_star_entries; ++i)
	    startab_mn[i] = mnlist[startab_mn[i]].offset;
	put_dw(f2, "wtab2", startab_mn, n_star_entries);
	fprintf(f2, "N_WTAB\tequ\t%d" CRLF, n_star_entries);

	fputs(CRLF ";\tThis is the table for operands which have a different "
		"mnemonic for" CRLF ";\ttheir 32 bit versions." CRLF CRLF, f2);
	put_dw(f2, "ltab1", slashtab_seq, n_slash_entries);
	for (i = 0; i < n_slash_entries; ++i)
	    slashtab_mn[i] = mnlist[slashtab_mn[i]].offset;
	put_dw(f2, "ltab2", slashtab_mn, n_slash_entries);
	fprintf(f2, "N_LTAB\tequ\t%d" CRLF, n_slash_entries);

	fputs(CRLF ";\tThis is the table of lockable instructions" CRLF CRLF,
	    f2);
	put_dw(f2, "locktab", locktab, n_locktab);
	fprintf(f2, "N_LOCK\tequ\t%d" CRLF, n_locktab);

	/*
	 * Print out miscellaneous equates.
	 */

	fprintf(f2, CRLF ";\tEquates used in the assembly-language code." CRLF
	    CRLF "SPARSE_BASE\tequ\t%d" CRLF
	    CRLF "SFPGROUP3\tequ\t%d" CRLF CRLF, SPARSE_BASE, SFPGROUP(3));
}

int cdecl
main() {
	FILE	*f1;
	FILE	*f2;
	int	offset;

	/*
	 * Read in the key dictionary.
	 */

	f1 = openread("instr.key");
	offset = OPTYPES + 1;
	while (getline(f1)) {
	    char *p = line;
	    int i;

	    if (n_keys >= MAX_OL_TYPES)
		fail("Too many keys.");
	    olkeydict[n_keys].key = getkey(&p);
	    p = skipwhite(p);
	    for (i = 0;; ++i) {
		if (i >= n_ol_types) {
		    char *q = xmalloc(strlen(p) + 1, "operand type name");

		    strcpy(q, p);
		    if (n_ol_types >= MAX_OL_TYPES)
			fail("Too many operand list types.");
		    olnames[n_ol_types] = q;
		    oloffset[n_ol_types] = offset;
		    for (;;) {
			++offset;
			q = strchr(q, ',');
			if (q == NULL) break;
			++q;
		    }
		    ++offset;
		    ++n_ol_types;
		}
		if (strcmp(p, olnames[i]) == 0)
		    break;
	    }
	    olkeydict[n_keys].value = i;
	    olkeydict[n_keys].width = 1;
	    if (strstr(p, "OP_ALL") != NULL)
		olkeydict[n_keys].width = 2;
	    else if (strstr(p, "OP_R_ADD") != NULL)
		olkeydict[n_keys].width = 8;
	    ++n_keys;
	}
	fclose(f1);
	if (offset >= 256) {
	    fprintf(stderr, "%d bytes of operand lists.  That's too many.\n",
		offset);
	    exit(1);
	}

	/*
	 * Read in the ordering relations.
	 */

	f1 = openread("instr.ord");
	while (getline(f1)) {
	    char *p = line;

	    if (n_ords >= MAX_N_ORDS)
		fail ("Too many ordering restrictions.");
	    keyord1[n_ords] = lookupkey(getkey(&p));
	    p = skipwhite(p);
	    keyord2[n_ords] = lookupkey(getkey(&p));
	    if (*p != '\0')
		fail("Syntax error in ordering file.");
	    ++n_ords;
	}
	fclose(f1);

	/*
	 * Do the main processing.
	 */

	f1 = openread("instr.set");
	read_is(f1);
	fclose(f1);

	/*
	 * Write the file.
	 */

	f1 = openread("debug.a86");
	f2 = fopen("debug.tmp", "w");
	if (f2 == NULL) {
	    perror("debug.tmp");
	    exit(1);
	}

	do {
	    if (fgets(line, LINELEN, f1) == NULL) {
		fputs("Couldn't find beginning marker line\n", stderr);
		exit(1);
	    }
	    fputs(line, f2);
	}
	while (strcmp(line, MARKLINE1) != 0);

	dumptables(f2);

	do {
	    if (fgets(line, LINELEN, f1) == NULL) {
		fputs("Couldn't find ending marker line\n", stderr);
		exit(1);
	    }
	}
	while (strcmp(line, MARKLINE2) != 0);

	do
	    fputs(line, f2);
	while (fgets(line, LINELEN, f1) != NULL);

	fclose(f1);    
	fclose(f2);

	/*
	 * Move the file to its original position.
	 */

#if	DOS
	if (unlink("debug.old") == -1 && errno != ENOFILE) {
	    perror("delete debug.old");
	    return 1;
	}
#endif
	if (rename("debug.a86", "debug.old") == -1) {
	    perror("rename debug.a86 -> debug.old");
	    return 1;
	}
	if (rename("debug.tmp", "debug.a86") == -1) {
	    perror("rename debug.tmp -> debug.a86");
	    return 1;
	}

	puts("Done.");

	return 0;
}
[ RETURN TO DIRECTORY ]