668 lines
11 KiB
C
668 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');
|
|
}
|
|
}
|