667 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			667 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 | |
|  * See the copyright notice in the ACK home directory, in the file "Copyright".
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "ip_spec.h"
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <stdarg.h>
 | |
| #include <string.h>
 | |
| #include "em_spec.h"
 | |
| #include "em_flag.h"
 | |
| 
 | |
| #ifndef NORCSID
 | |
| static char rcs_id[] = "$Id$";
 | |
| #endif
 | |
| 
 | |
| /* This program reads the human readable interpreter specification
 | |
|  and produces a efficient machine representation that can be
 | |
|  translated by a C-compiler.
 | |
|  */
 | |
| 
 | |
| #define NOTAB   600    /* The max no of interpreter specs */
 | |
| #define ESCAP1  256
 | |
| #define ESCAP2	257
 | |
| 
 | |
| struct opform intable[NOTAB];
 | |
| struct opform *lastform = intable - 1;
 | |
| 
 | |
| int nerror = 0;
 | |
| int atend = 0;
 | |
| int line = 1;
 | |
| 
 | |
| extern char em_mnem[][4];
 | |
| char esca1[] = "escape1";
 | |
| char esca2[] = "escape2";
 | |
| #define ename(no)       ((no)==ESCAP1?esca1:(no)==ESCAP2?esca2:em_mnem[(no)])
 | |
| 
 | |
| extern char em_flag[];
 | |
| 
 | |
| char *ident(void);
 | |
| void check(int);
 | |
| int decflag(char *);
 | |
| void checkall(void);
 | |
| void chkc(int, int, int, int);
 | |
| void ckop(int, int, int, int);
 | |
| int readchar(void);
 | |
| void pushback(int);
 | |
| void writeout(void);
 | |
| void prx(register int, int, int);
 | |
| void readin(void);
 | |
| int getmnem(char *);
 | |
| void error(char *str, ...);
 | |
| void mess(char *str, ...);
 | |
| void fatal(char *str, ...);
 | |
| 
 | |
| 
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
| 	if (argc > 1)
 | |
| 	{
 | |
| 		if (freopen(argv[1], "r", stdin) == NULL)
 | |
| 		{
 | |
| 			fatal("Cannot open %s", argv[1]);
 | |
| 		}
 | |
| 	}
 | |
| 	if (argc > 2)
 | |
| 	{
 | |
| 		if (freopen(argv[2], "w", stdout) == NULL)
 | |
| 		{
 | |
| 			fatal("Cannot create %s", argv[2]);
 | |
| 		}
 | |
| 	}
 | |
| 	if (argc > 3)
 | |
| 	{
 | |
| 		fatal("%s [ file [ file ] ]", argv[0]);
 | |
| 	}
 | |
| 	atend = 0;
 | |
| 	readin();
 | |
| 	atend = 1;
 | |
| 	checkall();
 | |
| 	if (nerror == 0)
 | |
| 	{
 | |
| 		writeout();
 | |
| 	}
 | |
| 	exit(nerror);
 | |
| }
 | |
| 
 | |
| void readin(void)
 | |
| {
 | |
| 	register struct opform *nextform;
 | |
| 	char *firstid;
 | |
| 
 | |
| 	for (nextform = intable; !feof(stdin) && nextform < &intable[NOTAB];)
 | |
| 	{
 | |
| 		firstid = ident();
 | |
| 		if (*firstid == '\n' || feof(stdin))
 | |
| 			continue;
 | |
| 		lastform = nextform;
 | |
| 		nextform->i_opcode = getmnem(firstid);
 | |
| 		nextform->i_flag = decflag(ident());
 | |
| 		switch (nextform->i_flag & OPTYPE)
 | |
| 		{
 | |
| 			case OPMINI:
 | |
| 			case OPSHORT:
 | |
| 				nextform->i_num = atoi(ident());
 | |
| 				break;
 | |
| 		}
 | |
| 		nextform->i_low = atoi(ident());
 | |
| 		if (*ident() != '\n')
 | |
| 		{
 | |
| 			int c;
 | |
| 			error("End of line expected");
 | |
| 			while ((c = readchar()) != '\n' && c != EOF)
 | |
| 				;
 | |
| 		}
 | |
| 		nextform++;
 | |
| 	}
 | |
| 	if (!feof(stdin))
 | |
| 		fatal("Internal table too small");
 | |
| }
 | |
| 
 | |
| char *ident(void)
 | |
| {
 | |
| 	/* skip spaces and tabs, anything up to space,tab or eof is
 | |
| 	 a identifier.
 | |
| 	 Anything from # to end-of-line is an end-of-line.
 | |
| 	 End-of-line is an identifier all by itself.
 | |
| 	 */
 | |
| 
 | |
| 	static char array[200];
 | |
| 	register int c;
 | |
| 	register char *cc;
 | |
| 
 | |
| 	do
 | |
| 	{
 | |
| 		c = readchar();
 | |
| 	} while (c == ' ' || c == '\t');
 | |
| 	for (cc = array; cc < &array[(sizeof array) - 1]; cc++)
 | |
| 	{
 | |
| 		if (c == '#')
 | |
| 		{
 | |
| 			do
 | |
| 			{
 | |
| 				c = readchar();
 | |
| 			} while (c != '\n' && c != EOF);
 | |
| 		}
 | |
| 		*cc = c;
 | |
| 		if (c == '\n' && cc == array)
 | |
| 			break;
 | |
| 		c = readchar();
 | |
| 		if (c == '\n')
 | |
| 		{
 | |
| 			pushback(c);
 | |
| 			break;
 | |
| 		}
 | |
| 		if (c == ' ' || c == '\t' || c == EOF)
 | |
| 			break;
 | |
| 	}
 | |
| 	*++cc = 0;
 | |
| 	return array;
 | |
| }
 | |
| 
 | |
| int getmnem(char *str)
 | |
| {
 | |
| 	char (*ptr)[4];
 | |
| 
 | |
| 	for (ptr = em_mnem; *ptr <= &em_mnem[sp_lmnem - sp_fmnem][0]; ptr++)
 | |
| 	{
 | |
| 		if (strcmp(*ptr, str) == 0)
 | |
| 			return (ptr - em_mnem);
 | |
| 	}
 | |
| 	error("Illegal mnemonic");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void error(char *str, ...)
 | |
| 	/* VARARGS1 */
 | |
| {
 | |
| 	if (!atend)
 | |
| 		fprintf(stderr, "line %d: ", line);
 | |
| 
 | |
| 	va_list ap;
 | |
| 	va_start(ap, str);
 | |
| 	vfprintf(stderr, str, ap);
 | |
| 	va_end(ap);
 | |
| 	fprintf(stderr, "\n");
 | |
| 	nerror++;
 | |
| }
 | |
| 
 | |
| void mess(char *str, ...)
 | |
| 	/* VARARGS1 */
 | |
| {
 | |
| 	if (!atend)
 | |
| 		fprintf(stderr, "line %d: ", line);
 | |
| 	va_list ap;
 | |
| 	va_start(ap, str);
 | |
| 	vfprintf(stderr, str, ap);
 | |
| 	va_end(ap);
 | |
| 	fprintf(stderr, "\n");
 | |
| }
 | |
| 
 | |
| void fatal(char *str, ...)
 | |
| 	/* VARARGS1 */
 | |
| {
 | |
| 	va_list ap;
 | |
| 	va_start(ap, str);
 | |
| 	error(str, ap);
 | |
| 	va_end(ap);
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| #define ILLGL   -1
 | |
| 
 | |
| void check(int val)
 | |
| {
 | |
| 	if (val != ILLGL)
 | |
| 		error("Illegal flag combination");
 | |
| }
 | |
| 
 | |
| int decflag(char *str)
 | |
| {
 | |
| 	int type;
 | |
| 	int escape;
 | |
| 	int range;
 | |
| 	int wordm;
 | |
| 	int notzero;
 | |
| 
 | |
| 	type = escape = range = wordm = notzero = ILLGL;
 | |
| 	while (*str)
 | |
| 		switch (*str++)
 | |
| 		{
 | |
| 			case 'm':
 | |
| 				check(type);
 | |
| 				type = OPMINI;
 | |
| 				break;
 | |
| 			case 's':
 | |
| 				check(type);
 | |
| 				type = OPSHORT;
 | |
| 				break;
 | |
| 			case '-':
 | |
| 				check(type);
 | |
| 				type = OPNO;
 | |
| 				break;
 | |
| 			case '1':
 | |
| 				check(type);
 | |
| 				type = OP8;
 | |
| 				break;
 | |
| 			case '2':
 | |
| 				check(type);
 | |
| 				type = OP16;
 | |
| 				break;
 | |
| 			case '4':
 | |
| 				check(type);
 | |
| 				type = OP32;
 | |
| 				break;
 | |
| 			case '8':
 | |
| 				check(type);
 | |
| 				type = OP64;
 | |
| 				break;
 | |
| 			case 'u':
 | |
| 				check(type);
 | |
| 				type = OP16U;
 | |
| 				break;
 | |
| 			case 'e':
 | |
| 				check(escape);
 | |
| 				escape = 0;
 | |
| 				break;
 | |
| 			case 'N':
 | |
| 				check(range);
 | |
| 				range = 2;
 | |
| 				break;
 | |
| 			case 'P':
 | |
| 				check(range);
 | |
| 				range = 1;
 | |
| 				break;
 | |
| 			case 'w':
 | |
| 				check(wordm);
 | |
| 				wordm = 0;
 | |
| 				break;
 | |
| 			case 'o':
 | |
| 				check(notzero);
 | |
| 				notzero = 0;
 | |
| 				break;
 | |
| 			default:
 | |
| 				error("Unknown flag");
 | |
| 		}
 | |
| 	if (type == ILLGL)
 | |
| 		error("Type must be specified");
 | |
| 	switch (type)
 | |
| 	{
 | |
| 		case OP64:
 | |
| 		case OP32:
 | |
| 			if (escape != ILLGL)
 | |
| 				error("Conflicting escapes");
 | |
| 			escape = ILLGL;
 | |
| 		case OP16:
 | |
| 		case OP16U:
 | |
| 		case OP8:
 | |
| 		case OPSHORT:
 | |
| 		case OPNO:
 | |
| 			if (notzero != ILLGL)
 | |
| 				mess("Improbable OPNZ");
 | |
| 			if (type == OPNO && range != ILLGL)
 | |
| 			{
 | |
| 				mess("No operand in range");
 | |
| 			}
 | |
| 	}
 | |
| 	if (escape != ILLGL)
 | |
| 		type |= OPESC;
 | |
| 	if (wordm != ILLGL)
 | |
| 		type |= OPWORD;
 | |
| 	switch (range)
 | |
| 	{
 | |
| 		case ILLGL:
 | |
| 			type |= OP_BOTH;
 | |
| 			if (type == OPMINI || type == OPSHORT)
 | |
| 				error("Minies and shorties must have P or N");
 | |
| 			break;
 | |
| 		case 1:
 | |
| 			type |= OP_POS;
 | |
| 			break;
 | |
| 		case 2:
 | |
| 			type |= OP_NEG;
 | |
| 			break;
 | |
| 	}
 | |
| 	if (notzero != ILLGL)
 | |
| 		type |= OPNZ;
 | |
| 	return type;
 | |
| }
 | |
| 
 | |
| /* ----------- checking --------------*/
 | |
| 
 | |
| int ecodes[256], codes[256], lcodes[256];
 | |
| char eflags[256], flags[256], lflags[256];
 | |
| int elows[256], lows[256], llows[256];
 | |
| 
 | |
| #define NMNEM   (sp_lmnem-sp_fmnem+1)
 | |
| #define MUST    1
 | |
| #define MAY     2
 | |
| #define FORB    3
 | |
| 
 | |
| char negc[NMNEM], zc[NMNEM], posc[NMNEM], lnegc[NMNEM], lposc[NMNEM];
 | |
| 
 | |
| void checkall(void)
 | |
| {
 | |
| 	register int i, flag;
 | |
| 	register struct opform *next;
 | |
| 	int opc, low;
 | |
| 
 | |
| 	for (i = 0; i < NMNEM; i++)
 | |
| 		negc[i] = zc[i] = posc[i] = 0;
 | |
| 	for (i = 0; i < 256; i++)
 | |
| 		lcodes[i] = codes[i] = ecodes[i] = -1;
 | |
| 	codes[254] = ESCAP1;
 | |
| 	codes[255] = ESCAP2;
 | |
| 
 | |
| 	atend = 0;
 | |
| 	line = 0;
 | |
| 	for (next = intable; next <= lastform; next++)
 | |
| 	{
 | |
| 		line++;
 | |
| 		flag = next->i_flag & 0377;
 | |
| 		opc = next->i_opcode & 0377;
 | |
| 		low = next->i_low & 0377;
 | |
| 		chkc(flag, low, opc, low);
 | |
| 		switch (flag & OPTYPE)
 | |
| 		{
 | |
| 			case OPNO:
 | |
| 				zc[opc]++;
 | |
| 				break;
 | |
| 			case OPMINI:
 | |
| 			case OPSHORT:
 | |
| 				for (i = 1; i < ((next->i_num) & 0377); i++)
 | |
| 				{
 | |
| 					chkc(flag, low + i, opc, low);
 | |
| 				}
 | |
| 				if (!(em_flag[opc] & PAR_G) && (flag & OPRANGE) == OP_BOTH)
 | |
| 				{
 | |
| 					mess("Mini's and shorties should have P or N");
 | |
| 				}
 | |
| 				break;
 | |
| 			case OP8:
 | |
| 				error("OP8 is removed");
 | |
| 				break;
 | |
| 			case OP16:
 | |
| 				if (flag & OP_NEG)
 | |
| 					negc[opc]++;
 | |
| 				else if (flag & OP_POS)
 | |
| 					posc[opc]++;
 | |
| 				break;
 | |
| 			case OP32:
 | |
| 				if (flag & OP_NEG)
 | |
| 					lnegc[opc]++;
 | |
| 				else if (flag & OP_POS)
 | |
| 					lposc[opc]++;
 | |
| 				break;
 | |
| 			case OP16U:
 | |
| 				break;
 | |
| 			default:
 | |
| 				error("Illegal type");
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	atend = 1;
 | |
| 	for (i = 0; i < 256; i++)
 | |
| 		if (codes[i] == -1)
 | |
| 		{
 | |
| 			mess("interpreter opcode %d not used", i);
 | |
| 		}
 | |
| 	for (opc = 0; opc < NMNEM; opc++)
 | |
| 	{
 | |
| 		switch (em_flag[opc] & EM_PAR)
 | |
| 		{
 | |
| 			case PAR_NO:
 | |
| 				ckop(opc, MUST, FORB, FORB);
 | |
| 				break;
 | |
| 			case PAR_C:
 | |
| 			case PAR_D:
 | |
| 			case PAR_F:
 | |
| 			case PAR_B:
 | |
| 				ckop(opc, FORB, MAY, MAY);
 | |
| 				break;
 | |
| 			case PAR_N:
 | |
| 			case PAR_G:
 | |
| 			case PAR_S:
 | |
| 			case PAR_Z:
 | |
| 			case PAR_O:
 | |
| 			case PAR_P:
 | |
| 				ckop(opc, FORB, MAY, FORB);
 | |
| 				break;
 | |
| 			case PAR_R:
 | |
| 				ckop(opc, FORB, MAY, FORB);
 | |
| 				break;
 | |
| 			case PAR_L:
 | |
| 				ckop(opc, FORB, MUST, MUST);
 | |
| 				break;
 | |
| 			case PAR_W:
 | |
| 				ckop(opc, MUST, MAY, FORB);
 | |
| 				break;
 | |
| 			default:
 | |
| 				error("Unknown instruction type of %s", ename(opc));
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void chkc(int flag, int icode, int emc, int low)
 | |
| {
 | |
| 	if (flag & OPESC)
 | |
| 	{
 | |
| 		if (ecodes[icode] != -1)
 | |
| 		{
 | |
| 			mess("Escaped opcode %d used by %s and %s", icode, ename(emc),
 | |
| 					ename(ecodes[icode]));
 | |
| 		}
 | |
| 		ecodes[icode] = emc;
 | |
| 		eflags[icode] = flag;
 | |
| 		elows[icode] = low;
 | |
| 	}
 | |
| 	else
 | |
| 		switch (flag & OPTYPE)
 | |
| 		{
 | |
| 			default:
 | |
| 				if (codes[icode] != -1)
 | |
| 				{
 | |
| 					mess("Opcode %d used by %s and %s", icode, ename(emc),
 | |
| 							ename(codes[icode]));
 | |
| 				}
 | |
| 				codes[icode] = emc;
 | |
| 				flags[icode] = flag;
 | |
| 				lows[icode] = low;
 | |
| 				break;
 | |
| 			case OP32:
 | |
| 			case OP64:
 | |
| 				if (lcodes[icode] != -1)
 | |
| 				{
 | |
| 					mess("Long opcode %d used by %s and %s", icode, ename(emc),
 | |
| 							ename(lcodes[icode]));
 | |
| 				}
 | |
| 				lcodes[icode] = emc;
 | |
| 				lflags[icode] = flag;
 | |
| 				llows[icode] = low;
 | |
| 				break;
 | |
| 		}
 | |
| }
 | |
| 
 | |
| void ckop(int emc, int zf, int pf, int nf)
 | |
| {
 | |
| 	if (zc[emc] > 1)
 | |
| 		mess("More then one OPNO for %s", ename(emc));
 | |
| 	if (posc[emc] > 1)
 | |
| 		mess("More then one OP16(pos) for %s", ename(emc));
 | |
| 	if (negc[emc] > 1)
 | |
| 		mess("More then one OP16(neg) for %s", ename(emc));
 | |
| 	if (lposc[emc] > 1)
 | |
| 		mess("More then one OP32(pos) for %s", ename(emc));
 | |
| 	if (lnegc[emc] > 1)
 | |
| 		mess("More then one OP32(neg) for %s", ename(emc));
 | |
| 	switch (zf)
 | |
| 	{
 | |
| 		case MUST:
 | |
| 			if (zc[emc] == 0)
 | |
| 				mess("No OPNO for %s", ename(emc));
 | |
| 			break;
 | |
| 		case FORB:
 | |
| 			if (zc[emc] == 1)
 | |
| 				mess("Forbidden OPNO for %s", ename(emc));
 | |
| 			break;
 | |
| 	}
 | |
| 	switch (pf)
 | |
| 	{
 | |
| 		case MUST:
 | |
| 			if (posc[emc] == 0)
 | |
| 				mess("No OP16(pos) for %s", ename(emc));
 | |
| 			break;
 | |
| 		case FORB:
 | |
| 			if (posc[emc] == 1)
 | |
| 				mess("Forbidden OP16(pos) for %s", ename(emc));
 | |
| 			break;
 | |
| 	}
 | |
| 	switch (nf)
 | |
| 	{
 | |
| 		case MUST:
 | |
| 			if (negc[emc] == 0)
 | |
| 				mess("No OP16(neg) for %s", ename(emc));
 | |
| 			break;
 | |
| 		case FORB:
 | |
| 			if (negc[emc] == 1)
 | |
| 				mess("Forbidden OP16(neg) for %s", ename(emc));
 | |
| 			break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int pushchar;
 | |
| static int pushf;
 | |
| 
 | |
| int readchar(void)
 | |
| {
 | |
| 	int c;
 | |
| 
 | |
| 	if (pushf)
 | |
| 	{
 | |
| 		pushf = 0;
 | |
| 		c = pushchar;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (feof(stdin))
 | |
| 			return EOF;
 | |
| 		c = getc(stdin);
 | |
| 	}
 | |
| 	if (c == '\n')
 | |
| 		line++;
 | |
| 	return c;
 | |
| }
 | |
| 
 | |
| void pushback(int c)
 | |
| {
 | |
| 	if (pushf)
 | |
| 	{
 | |
| 		fatal("Double pushback");
 | |
| 	}
 | |
| 	pushf++;
 | |
| 	pushchar = c;
 | |
| 	if (c == '\n')
 | |
| 		line--;
 | |
| }
 | |
| 
 | |
| void writeout(void)
 | |
| {
 | |
| 	register int i;
 | |
| 
 | |
| 	printf("DISPATCH1");
 | |
| 	for (i = 0; i < 256;)
 | |
| 	{
 | |
| 		if (!(i % 8))
 | |
| 			printf("\n%d", i);
 | |
| 		printf("\t%s", ename(codes[i]));
 | |
| 		if (i < 254)
 | |
| 		{
 | |
| 			prx(flags[i], lows[i], i);
 | |
| 		}
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	printf("\nDISPATCH2");
 | |
| 	for (i = 0; i < 256;)
 | |
| 	{
 | |
| 		if (ecodes[i] != -1)
 | |
| 		{
 | |
| 			if (!(i % 8))
 | |
| 				printf("\n%d", i);
 | |
| 			printf("\t%s", ename(ecodes[i]));
 | |
| 			prx(eflags[i], elows[i], i);
 | |
| 		}
 | |
| 		else
 | |
| 			break;
 | |
| 		i++;
 | |
| 	}
 | |
| 
 | |
| 	printf("\nDISPATCH3");
 | |
| 	i = 0;
 | |
| 	while (lcodes[i] != -1)
 | |
| 	{
 | |
| 		if (!(i % 8))
 | |
| 			printf("\n%d", i);
 | |
| 		printf("\t%s", ename(lcodes[i]));
 | |
| 		prx(lflags[i], llows[i], i);
 | |
| 		i++;
 | |
| 	}
 | |
| 	while (i++ % 8)
 | |
| 		putchar('\t');
 | |
| 	putchar('\n');
 | |
| }
 | |
| 
 | |
| void prx(register int flg, int low, int opc)
 | |
| {
 | |
| 	int arg = opc - low;
 | |
| 
 | |
| 	putchar('.');
 | |
| 	switch (flg & OPTYPE)
 | |
| 	{
 | |
| 		case OPNO:
 | |
| 			putchar('z');
 | |
| 			break;
 | |
| 		case OP16U:
 | |
| 			putchar('u');
 | |
| 			break;
 | |
| 		case OP16:
 | |
| 			if (flg & OP_POS)
 | |
| 				putchar('p');
 | |
| 			else if (flg & OP_NEG)
 | |
| 				putchar('n');
 | |
| 			else
 | |
| 				putchar('l');
 | |
| 			if (flg & OPWORD)
 | |
| 				putchar('w');
 | |
| 			break;
 | |
| 		case OP32:
 | |
| 			if (flg & OP_POS)
 | |
| 				putchar('P');
 | |
| 			else if (flg & OP_NEG)
 | |
| 				putchar('N');
 | |
| 			else
 | |
| 				putchar('L');
 | |
| 			if (flg & OPWORD)
 | |
| 				putchar('w');
 | |
| 			break;
 | |
| 		case OPSHORT:
 | |
| 			if (flg & OPWORD)
 | |
| 				putchar('w');
 | |
| 			else
 | |
| 				putchar('s');
 | |
| 			/* fall through */
 | |
| 		case OPMINI:
 | |
| 			if (flg & OPNZ)
 | |
| 				arg++;
 | |
| 			if (flg & OP_NEG)
 | |
| 				arg = -arg - 1;
 | |
| 			printf("%d", arg);
 | |
| 			if ((flg & OPTYPE) == OPMINI && (flg & OPWORD))
 | |
| 				putchar('W');
 | |
| 	}
 | |
| }
 |