DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T z

⟦e85e52029⟧ TextFile

    Length: 50295 (0xc477)
    Types: TextFile
    Names: »zmac.y«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/zmac/zmac.y« 

TextFile

%{
/*
 *  zmac -- macro cross-assembler for the Zilog Z80 microprocessor
 *
 *  Bruce Norskog	4/78
 *
 *  Last modification  1-18-87 by cdk
 *  This assembler is modeled after the Intel 8080 macro cross-assembler
 *  for the Intel 8080 by Ken Borgendale.  The major features are:
 *	1.  Full macro capabilities
 *	2.  Conditional assembly
 *	3.  A very flexible set of listing options and pseudo-ops
 *	4.  Symbol table output
 *	5.  Error report
 *	6.  Elimination of sequential searching
 *	7.  Commenting of source
 *	8.  Facilities for system definiton files
 *
 * Revision History:
 *
 * jrp	3-8-82	Converted to run on Vax, updated syntax to conform better
 *		to the Zilog standard.
 *
 * jrp	3-15-82	Added underscore as a character type in the lex table
 *		'numpart' (0x5F).
 *
 *		Changed maximum number of characters in a label to 15
 *		from 7. Note that 'putsymtab' uses this value inside
 *		of a quoted string, so we use 15.
 *
 * jrp	2-15-83	Fixed 'getlocal' to return better local labels. It used
 *		to crash after 6 invocations.
 *
 * jrp	6-7-83	Fixed bug in the ADD IX,... instruction.
 *
 * jrp	5-11-84	Added code to print unused labels out with the symbol table
 *		Also sped up the macro processor by using stdio.
 *
 * jrp 5-22-84	Added include files ala ormac
 *
 * jrp 8-27-84	Added PHASE/DEPHASE commands
 *
 * cdk 9-20-86	Converted to run on a Pyramid.  This meant changing yylval
 *		to be a %union, and then putting in the appropriate
 *		typecasts where ints are pointers are used interchangeably.
 *		The current version still probably won't run on machines where
 *		sizeof(int) != sizeof(char *).
 *		Also changed emit() to use varargs, and got rid of the
 *		old style = in front of yacc action code.
 *			-Colin Kelley  vu-vlsi!colin
 *
 * cdk 10-2-86	Added some more typecasts to keep lint a little happier.
 *		Removed several unused variables.  Changed most vars
 *		declared as char to int, since many of them were being
 *		compared with -1!  I still don't know what's going on with
 *		est[][] being malloc'd and free'd everywhere...it looks pretty
 *		fishy...
 *
 * cdk 1-18-87  Added MIO code to emulate 'mfile' using malloc()'d memory.
 *		This was needed to get the code to work when compiled under
 *		MSC 4.0 on a PC, and it's probably faster anyway.
 *
 * cdk 2-5-87	Added 'cmp' as a synonym for 'cp', 'jmp' as a synonym for
 *		'jp', and added tolerance of accumulator specification for arithmetic
 *		and logical instructions.  (For example, 'or a,12' is now accepted,
 *		same as 'or 12'.)
 */


#define MIO		/* use emulation routines from mio.c */

#include <stdio.h>
#ifdef MSDOS
#include <fcntl.h>
#else
#include <sys/file.h>	/* for open() calls */
#endif

#ifdef vax11c
#define unlink(filename) delete(filename)
#endif

#ifdef MIO
FILE *mfopen();
#else
#define mfopen(filename,mode) fopen(filename,mode)
#define mfclose(filename,mode) fclose(filename,mode) 
#define mfputc(c,f) putc(c,f)
#define mfgetc(f) getc(f)
#define mfseek(f,loc,origin) fseek(f,loc,origin)
#define mfread(ptr,size,nitems,f) fread(ptr,size,nitems,f)
#define mfwrite(ptr,size,nitems,f) fread(ptr,size,nitems,f)
#endif /* MIO */

/*
 * DEBUG turns on pass reporting.
 * Macro debug and Token debug enables.
#define	DEBUG
#define	M_DEBUG	
#define	T_DEBUG
 */

#define ITEMTABLESIZE	2000
#define TEMPBUFSIZE	200
#define LINEBUFFERSIZE	200
#define EMITBUFFERSIZE	200
#define MAXSYMBOLSIZE	15
#define IFSTACKSIZE	20
#define MAXIFS		150
#define TITLELEN	50
#define BINPERLINE	16
#define	PARMMAX		25
#define MAXEXP		25
#define SYMMAJIC	07203
#define	NEST_IN		8


#define loop	for(;;)

yyerror(err)
char *err;
{}		/* we will do our own error printing */

struct	item	{
	char	*i_string;
	int	i_value;
	int	i_token;
	int	i_uses;
};

FILE	*fout,
	*fbuf,
	*fin[NEST_IN],
	*now_file ;

char *malloc() ;

int	pass2;	/*set when pass one completed*/
int	dollarsign ;	/* location counter */
int	olddollar ;	/* kept to put out binary */

/* program counter save for PHASE/DEPHASE */
int	phdollar, phbegin, phaseflag ;

char	*src_name[NEST_IN] ;
int	linein[NEST_IN] ;
int	now_in ;



#define bflag	0	/* balance error */
#define eflag	1	/* expression error */
#define fflag	2	/* format error */
#define iflag	3	/* bad digits */
#define mflag	4	/* multiply defined */
#define pflag	5	/* phase error */
#define uflag	6	/* undeclared used */
#define vflag	7	/* value out of range */
#define oflag	8	/* phase/dephase error */

#define FLAGS	9	/* number of flags */

char	err[FLAGS];
int	keeperr[FLAGS];
char	errlet[]="BEFIMPUVO";
char	*errname[]={
	"Balance",
	"Expression",
	"Format",
	"Digit",
	"Mult. def.",
	"Phase",
	"Undeclared",
	"Value",
	"Phase/Dephase",
};


char	linebuf[LINEBUFFERSIZE];
char	*lineptr;
char	*linemax = &linebuf[LINEBUFFERSIZE];

char	outbin[BINPERLINE];
char	*outbinp = outbin;
char	*outbinm = &outbin[BINPERLINE];

char	emitbuf[EMITBUFFERSIZE];
char	*emitptr;

char	ifstack[IFSTACKSIZE];
char	*ifptr;
char	*ifstmax = &ifstack[IFSTACKSIZE-1];


char	expif[MAXIFS];
char	*expifp;
char	*expifmax = &expif[MAXIFS];

char	hexadec[] = "0123456789ABCDEF" ;
char	*expstack[MAXEXP];
int	expptr;


int	nitems;
int	linecnt;
int	nbytes;
int	invented;


char	tempbuf[TEMPBUFSIZE];
char	*tempmax = &tempbuf[TEMPBUFSIZE-1];

char	inmlex;
char	arg_flag;
char	quoteflag;
int	parm_number;
int	exp_number;
char	symlong[] = "Symbol too long";

int	disp;
#define FLOC	PARMMAX
#define TEMPNUM	PARMMAX+1
char	**est;
char	**est2;

char	*floc;
int	mfptr;
FILE	*mfile;

char	*writesyms;


char	*title;
char	titlespace[TITLELEN];
char	*timp,*ctime();
char	*sourcef;
char	src[15];
char	bin[15];
char	mtmp[15];
char	listf[15];

char	bopt = 1,
	edef = 1,
	eopt = 1,
	fdef = 0,
	fopt = 0,
	gdef = 1,
	gopt = 1,
	iopt = 0 ,	/* list include files */
	lstoff = 0,
	lston = 0,	/* flag to force listing on */
	lopt = 0,
	mdef = 0,
	mopt = 0,
	nopt = 1 ,	/* line numbers on as default */
	oopt = 0,
	popt = 1,	/* form feed as default page eject */
	sopt = 0,	/* turn on symbol table listing */
	topt = 1;
	saveopt;

char	xeq_flag = 0;
int	xeq;

long	now;
int	line;
int	page = 1;

struct stab {
	char	t_name[MAXSYMBOLSIZE+1];
	int	t_value;
	int	t_token;
};

/*
 *  push back character
 */
int	peekc;


/*
 *  add a character to the output line buffer
 */
addtoline(ac)
int	ac;
{
	/* check for EOF from stdio */
	if (ac == -1)
		ac = 0 ;
	if (inmlex)
		return(ac);
	if (lineptr >= linemax)
		error("line buffer overflow");
	*lineptr++ = ac;
	return(ac);
}

#include <varargs.h>

/*
 *  put values in buffer for outputing
 */

/*VARARGS*/
/*ARGSUSED*/
emit(va_alist)
va_dcl
{	
	register int bytes;
	va_list ap;
	va_start(ap);

	bytes = va_arg(ap,int);

	while	(--bytes >= 0)
		if (emitptr >= &emitbuf[EMITBUFFERSIZE])
			error("emit buffer overflow");
		else {
			*emitptr++ = va_arg(ap,int);
		}
	va_end(ap);
}


emit1(opcode,regvalh,data16,type)
int	opcode,regvalh,data16,type;
{
	if (regvalh & 0x8000) {
		if (type & 1 == 0 && (disp > 127 || disp < -128))
			err[vflag]++;
		switch(type) {
		case 0:
			if (opcode & 0x8000)
				emit(4, regvalh >> 8, opcode >> 8, disp, opcode);
			else
				emit(3, regvalh >> 8, opcode, disp);
			break;
		case 1:
			emit(2, regvalh >> 8, opcode);
			break;
		case 2:
			if (data16 > 255 || data16 < -128)
				err[vflag]++;
			emit(4, regvalh >> 8, opcode, disp, data16);
			break;
		case 5:
			emit(4, regvalh >> 8, opcode, data16, data16 >> 8);
		}
	} else
		switch(type) {
		case 0:
			if (opcode & 0100000)
				emit(2, opcode >> 8, opcode);
			else
				emit(1, opcode);
			break;
		case 1:
			if (opcode & 0100000)
				emit(2, opcode >> 8, opcode);
			else
				emit(1, opcode);
			break;
		case 2:
			if (data16 > 255 || data16 < -128)
				err[vflag]++;
			emit(2, opcode, data16);
			break;
		case 3:
			if (data16 >255 || data16 < -128)
				err[vflag]++;
			emit(2, opcode, data16);
			break;
		case 5:
			if (opcode & 0100000)
				emit(4, opcode >> 8, opcode, data16, data16 >> 8);
			else
				emit(3, opcode, data16, data16 >> 8);
		}
}




emitdad(rp1,rp2)
int rp1,rp2;
{
	if (rp1 & 0x8000)
		emit(2,rp1 >> 8, rp2 + 9);
	else
		emit(1,rp2 + 9);
}


emitjr(opcode,expr)
int	opcode,expr;
{
	disp = expr - dollarsign - 2;
	if (disp > 127 || disp < -128)
		err[vflag]++;
	emit(2, opcode, disp);
}




/*
 *  put out a byte of binary 
 */
putbin(v)
{
	if(!pass2 || !bopt) return;
	*outbinp++ = v;
	if (outbinp >= outbinm) flushbin();
}



/*
 *  output one line of binary in INTEL standard form
 */
flushbin()
{
	register  char *p;
	register check;

	if (!pass2 || !bopt)
		return;
	nbytes += outbinp-outbin;
	if (check = outbinp-outbin) {
		putc(':', fbuf);
		puthex(check, fbuf);
		puthex(olddollar>>8, fbuf);
		puthex(olddollar, fbuf);
		puthex(0, fbuf);
		check += (olddollar >> 8) + olddollar;
		olddollar += (outbinp-outbin);
		for (p=outbin; p<outbinp; p++) {
			puthex(*p, fbuf);
			check += *p;
		}
		puthex(256-check, fbuf);
		putc('\n', fbuf);
		outbinp = outbin;
	}
}



/*
 *  put out one byte of hex
 */
puthex(byte, buf)
char	byte;
FILE	*buf;
{
	putc(hexadec[(byte >> 4) & 017], buf);
	putc(hexadec[byte & 017], buf);
}

/*
 *  put out a line of output -- also put out binary
 */
list(optarg)
int	optarg;
{
	register char *	p;
	register int	i;
	int  lst;

	if (!expptr)
		linecnt++;
	addtoline('\0');
	if (pass2) {
		lst = iflist();
		if (lst) {
			lineout();
			if (nopt)
				fprintf(fout, "%4d:\t", linein[now_in]);
			puthex(optarg >> 8, fout);
			puthex(optarg, fout);
			fputs("  ", fout);
			for (p = emitbuf; (p < emitptr) && (p - emitbuf < 4); p++) {
				puthex(*p, fout);
			}
			for (i = 4 - (p-emitbuf); i > 0; i--)
				fputs("  ", fout);
			putc('\t', fout);
			fputs(linebuf, fout);
		}

		if (bopt) {
			for (p = emitbuf; p < emitptr; p++)
				putbin(*p);
		}


		p = emitbuf+4;
		while (lst && gopt && p < emitptr) {
			lineout();
			if (nopt) putc('\t', fout);
			fputs("      ", fout);
			for (i = 0; (i < 4) && (p < emitptr);i++) {
				puthex(*p, fout);
				p++;
			}
			putc('\n', fout);
		}


		lsterr2(lst);
	} else
		lsterr1();
	dollarsign += emitptr - emitbuf;
	emitptr = emitbuf;
	lineptr = linebuf;
}



/*
 *  keep track of line numbers and put out headers as necessary
 */
lineout()
{
	if (line == 60) {
		if (popt)
			putc('\014', fout);	/* send the form feed */
		else
			fputs("\n\n\n\n\n", fout);
		line = 0;
	}
	if (line == 0) {
		fprintf(fout, "\n\n%s %s\t%s\t Page %d\n\n\n",
			&timp[4], &timp[20], title, page++);
		line = 4;
	}
	line++;
}


/*
 *  cause a page eject
 */
eject()
{
	if (pass2 && iflist()) {
		if (popt) {
			putc('\014', fout);	/* send the form feed */
		} else {
			while (line < 65) {
				line++;
				putc('\n', fout);
			}
		}
	}
	line = 0;
}


/*
 *  space n lines on the list file
 */
space(n)
{
	int	i ;
	if (pass2 && iflist())
		for (i = 0; i<n; i++) {
			lineout();
			putc('\n', fout);
		}
}


/*
 *  Error handling - pass 1
 */
lsterr1() {
	register int i;
	if (topt)
		for (i = 0; i <= 4; i++)
			if (err[i]) {
				errorprt(i);
				err[i] = 0;
			}
}


/*
 *  Error handling - pass 2.
 */
lsterr2(lst)
int	lst;
{
	register int i;
	for (i=0; i<FLAGS; i++)
		if (err[i]) {
			if (lst) {
				lineout();
				putc(errlet[i], fout);
				putc('\n', fout);
			}
			err[i] = 0;
			keeperr[i]++;
			if (i > 4 && topt)
				errorprt(i);
		}

	fflush(fout);	/*to avoid putc(har) mix bug*/
}

/*
 *  print diagnostic to error terminal
 */
errorprt(errnum)
int	errnum;
{
	fprintf(stderr,"%d: %s error\n%s\n",
		linecnt, errname[errnum], linebuf) ;
	fflush(stderr) ;
	return ;
}


/*
 *  list without address -- for comments and if skipped lines
 */
list1()
{
	int lst;

	addtoline('\0');
	lineptr = linebuf;
	if (!expptr) linecnt++;
	if (pass2)
		if (lst = iflist()) {
			lineout();
			if (nopt)
				fprintf(fout, "%4d:\t", linein[now_in]);
			fprintf(fout, "\t\t%s", linebuf);
			lsterr2(lst);
		}
	else
		lsterr1();
}


/*
 *  see if listing is desired
 */
iflist()
{
	register  i, j;

	if (lston)
		return(1) ;
	if (lopt)
		return(0);
	if (*ifptr && !fopt)
		return(0);
	if (!lstoff && !expptr)
		return(1);
	j = 0;
	for (i=0; i<FLAGS; i++)
		if (err[i])
			j++;
	if (expptr)
		return(mopt || j);
	if (eopt && j)
		return(1);
	return(0);
}


%}

%union	{
	struct item *itemptr;
	int ival;
	char *cval;
	}

%token <cval> STRING
%token <itemptr> NOOPERAND
%token <itemptr> ARITHC
%token ADD
%token <itemptr> LOGICAL
%token <itemptr> BIT
%token CALL
%token <itemptr> INCDEC
%token <itemptr> DJNZ
%token EX
%token <itemptr> IM
%token PHASE
%token DEPHASE
%token <itemptr> IN
%token JP
%token <itemptr> JR
%token LD
%token <itemptr> OUT
%token <itemptr> PUSHPOP
%token <itemptr> RET
%token <itemptr> SHIFT
%token <itemptr> RST
%token <itemptr> REGNAME
%token <itemptr> ACC
%token <itemptr> C
%token <itemptr> RP
%token <itemptr> HL
%token <itemptr> INDEX
%token <itemptr> AF
%token <itemptr> SP
%token <itemptr> MISCREG
%token F
%token <itemptr> COND
%token <itemptr> SPCOND
%token <ival> NUMBER
%token <itemptr> UNDECLARED
%token END
%token ORG
%token DEFB
%token DEFS
%token DEFW
%token EQU
%token DEFL
%token <itemptr> LABEL
%token <itemptr> EQUATED
%token <itemptr> WASEQUATED
%token <itemptr> DEFLED
%token <itemptr> MULTDEF
%token <ival> MOD
%token <ival> SHL
%token <ival> SHR
%token <ival> NOT
%token IF
%token ENDIF
%token <itemptr> ARGPSEUDO
%token <itemptr> LIST
%token <itemptr> MINMAX
%token MACRO
%token <itemptr> MNAME
%token <itemptr> OLDMNAME
%token ARG
%token ENDM
%token MPARM
%token <ival> ONECHAR
%token <ival> TWOCHAR

%type <itemptr> label.part symbol
%type <ival> reg evenreg realreg mem pushable bcdesp bcdehlsp mar condition
%type <ival> spcondition parenexpr expression lxexpression

%left '|' '^'
%left '&'
%nonassoc NOT
%left '+' '-'
%left '*' '/' MOD SHL SHR
%left UNARY
%%

%{
char  *cp;
int  i;
%}

program:
	statements
|
	error	{	error("file bad");	}
;


statements:
	statement
|
	statements statement
|
	statements error	{
		fprintf(stderr,"statement error\n");
		err[fflag]++;
		quoteflag = 0;
		while(yychar != '\n' && yychar != '\0') yychar = yylex();
		list(dollarsign);
		yyclearin;yyerrok;
	}
;


statement:
	label.part '\n'	{ 
		if ($1) list(dollarsign);
		else  list1();
	}
|
	label.part operation '\n' {
		list(dollarsign);
	}
|
	symbol EQU expression '\n' {
		switch($1->i_token) {
		case UNDECLARED: case WASEQUATED:
			$1->i_token = EQUATED;
			$1->i_value = $3;
			break;
		default:
			err[mflag]++;
			$1->i_token = MULTDEF;
		}
		list($3);
	}
|
	symbol DEFL expression '\n' {
		switch($1->i_token) {
		case UNDECLARED: case DEFLED:
			$1->i_token = DEFLED;
			$1->i_value = $3;
			break;
		default:
			err[mflag]++;
			$1->i_token = MULTDEF;
		}
		list($3);
	}
|
	symbol MINMAX expression ',' expression '\n' {
		switch ($1->i_token) {
		case UNDECLARED: case DEFLED:
			$1->i_token = DEFLED;
			if ($2->i_value)	/* max */
				list($1->i_value = ($3 > $5? $3:$5));
			else list($1->i_value = ($3 < $5? $3:$5));
			break;
		default:
			err[mflag]++;
			$1->i_token = MULTDEF;
			list($1->i_value);
		}
	}
|
	IF expression '\n' {
		if (ifptr >= ifstmax)
			error("Too many ifs");
		else {
			if (pass2) {
				*++ifptr = *expifp++;
				if (*ifptr != !(yypv[2].ival)) err[pflag]++;
			} else {
				if (expifp >= expifmax)
					error("Too many ifs!");
				*expifp++ = !(yypv[2].ival);
				*++ifptr = !(yypv[2].ival);
			}
		}
		saveopt = fopt;
		fopt = 1;
		list(yypv[2].ival);
		fopt = saveopt;
	}
|
	ENDIF '\n' {
		if (ifptr == ifstack) err[bflag]++;
		else --ifptr;
		list1();
	}
|
	label.part END '\n' {
		list(dollarsign);
		peekc = 0;
	}
|
	label.part END expression '\n' {
		xeq_flag++;
		xeq = $3;
		list($3);
		peekc = 0;
	}
|
	label.part DEFS expression '\n' {
		if ($3 < 0) err[vflag]++;
		list(dollarsign);
		if ($3) {
			flushbin();
			dollarsign += $3;
			olddollar = dollarsign;
		}
	}
|
	ARGPSEUDO arg_on ARG  arg_off '\n' {
		list1();
		switch ($1->i_value) {

		case 0:		/* title */
			lineptr = linebuf;
			cp = tempbuf;
			title = titlespace;
			while ((*title++ = *cp++) && (title < &titlespace[TITLELEN]));
			*title = 0;
			title = titlespace;
			break;

		case 1:		/* rsym */
			if (pass2) break;
			insymtab(tempbuf);
			break;

		case 2:		/* wsym */
			writesyms = malloc(strlen(tempbuf)+1);
			strcpy(writesyms, tempbuf);
			break;
		case 3:		/* include file */
			next_source(tempbuf) ;
			break ;
		}
	}
|
	ARGPSEUDO arg_on '\n' arg_off {
		fprintf(stderr,"ARGPSEUDO error\n");
		err[fflag]++;
		list(dollarsign);
	}
|
	LIST '\n' {
		if ($1 != (struct item *) -1) $<ival>2 = 1;
		goto dolopt; }
|
	LIST expression '\n' {
	dolopt:
		linecnt++;
		if (pass2) {
			lineptr = linebuf;
			switch ($1->i_value) {
			case 0:	/* list */
				if ($2 < 0) lstoff = 1;
				if ($2 > 0) lstoff = 0;
				break;

			case 1:	/* eject */
				if ($2) eject();
				break;

			case 2:	/* space */
				if ((line + $2) > 60) eject();
				else space($2);
				break;

			case 3:	/* elist */
				eopt = edef;
				if ($2 < 0) eopt = 0;
				if ($2 > 0) eopt = 1;
				break;

			case 4:	/* fopt */
				fopt = fdef;
				if ($2 < 0) fopt = 0;
				if ($2 > 0) fopt = 1;
				break;

			case 5:	/* gopt */
				gopt = gdef;
				if ($2 < 0) gopt = 1;
				if ($2 > 0) gopt = 0;
				break;

			case 6: /* mopt */
				mopt = mdef;
				if ($2 < 0) mopt = 0;
				if ($2 > 0) mopt = 1;
			}
		}
	}
|
	UNDECLARED MACRO parm.list '\n' {
		$1->i_token = MNAME;
		$1->i_value = mfptr;
		mfseek(mfile, (long)mfptr, 0);
		list1();
		mlex() ;
		parm_number = 0;
	}
|
	OLDMNAME MACRO {
		$1->i_token = MNAME;
		while (yychar != ENDM && yychar) {
			while (yychar != '\n' && yychar)
				yychar = yylex();
			list1();
			yychar = yylex();
		}
		while (yychar != '\n' && yychar) yychar = yylex();
		list1();
		yychar = yylex();
	}
|
	label.part MNAME al arg.list '\n' {
	expand:
		$2->i_uses++ ;
		arg_flag = 0;
		parm_number = 0;
		list(dollarsign);
		expptr++;
		est = est2;
		est[FLOC] = floc;
		est[TEMPNUM] = (char *)exp_number++;
		floc = (char *)($2->i_value);
		mfseek(mfile, (long)floc, 0);
	}
;


label.part:
	/*empty*/
	 {	$$ = NULL;	}
|
	symbol ':' {
		switch($1->i_token) {
		case UNDECLARED:
			if (pass2)
				err[pflag]++;
			else {
				$1->i_token = LABEL;
				$1->i_value = dollarsign;
			}
			break;
		case LABEL:
			if (!pass2) {
				$1->i_token = MULTDEF;
				err[mflag]++;
			} else if ($1->i_value != dollarsign)
				err[pflag]++;
			break;
		default:
			err[mflag]++;
			$1->i_token = MULTDEF;
		}
	}
;


operation:
	NOOPERAND
		{ emit1($1->i_value, 0, 0, 1); }
|
	JP expression
		{ emit(3, 0303, $2, $2 >> 8);	}
|
	CALL expression
		{	emit(3, 0315, $2, $2 >> 8);	}
|
	RST	expression
		{ if ($2 > 7 || $2 < 0)
			err[vflag]++;
		emit(1, $1->i_value + (($2 & 7) << 3));
	}
|
	ADD ACC ',' expression
		{ emit1(0306, 0, $4, 3); }
|
	ARITHC ACC ',' expression
		{ emit1(0306 + ($1->i_value << 3), 0, $4, 3); }
|
	LOGICAL expression
		{ emit1(0306 | ($1->i_value << 3), 0, $2, 3); }
|
	LOGICAL ACC ',' expression	/* -cdk */
		{ emit1(0306 | ($1->i_value << 3), 0, $4, 3); }
|
	ADD ACC ',' reg
		{ emit1(0200 + ($4 & 0377), $4, 0, 0); }
|
	ARITHC ACC ',' reg
		{ emit1(0200 + ($1->i_value << 3) + ($4 & 0377), $4, 0, 0); }
|
	LOGICAL reg
		{ emit1(0200 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); }
|
	LOGICAL ACC ',' reg		/* -cdk */
		{ emit1(0200 + ($1->i_value << 3) + ($4 & 0377), $4, 0, 0); }
|
	SHIFT reg
		{ emit1(0145400 + ($1->i_value << 3) + ($2 & 0377), $2, 0, 0); }
|
	INCDEC	reg
		{ emit1($1->i_value + (($2 & 0377) << 3) + 4, $2, 0, 0); }
|
	ARITHC HL ',' bcdehlsp
		{ if ($1->i_value == 1)
				emit(2,0355,0112+$4);
			else
				emit(2,0355,0102+$4);
		}
|
	ADD mar ',' bcdesp
		{ emitdad($2,$4); }
|
	ADD mar ',' mar
		{
			if ($2 != $4) {
				fprintf(stderr,"ADD mar, mar error\n");
				err[fflag]++;
			}
			emitdad($2,$4);
		}
|
	INCDEC evenreg
		{ emit1(($1->i_value << 3) + ($2 & 0377) + 3, $2, 0, 1); }
|
	PUSHPOP pushable
		{ emit1($1->i_value + ($2 & 0377), $2, 0, 1); }
|
	BIT expression ',' reg
		{
			if ($2 < 0 || $2 > 7)
				err[vflag]++;
			emit1($1->i_value + (($2 & 7) << 3) + ($4 & 0377), $4, 0, 0);
		}
|
	JP condition ',' expression
		{ emit(3, 0302 + $2, $4, $4 >> 8); }
|
	JP '(' mar ')'
		{ emit1(0351, $3, 0, 1); }
|
	CALL condition ',' expression
		{ emit(3, 0304 + $2, $4, $4 >> 8); }
|
	JR expression
		{ emitjr(030,$2); }
|
	JR spcondition ',' expression
		{ emitjr($1->i_value + $2, $4); }
|
	DJNZ expression
		{ emitjr($1->i_value, $2); }
|
	RET
		{ emit(1, $1->i_value); }
|
	RET condition
		{ emit(1, 0300 + $2); }
|
	LD reg ',' reg
		{
			if (($2 & 0377) == 6 && ($4 & 0377) == 6) {
				fprintf(stderr,"LD reg, reg error\n");
				err[fflag]++;
			}
			emit1(0100 + (($2 & 7) << 3) + ($4 & 7),$2 | $4, 0, 0);
		}
|
	LD reg ',' expression
		{ emit1(6 + (($2 & 0377) << 3), $2, $4, 2); }
|
	LD reg ',' '(' RP ')'
		{	if ($2 != 7) {
				fprintf(stderr,"LD reg, (RP) error\n");
				err[fflag]++;
			}
			else emit(1, 012 + $5->i_value);
		}
|
	LD reg ',' parenexpr
		{
			if ($2 != 7) {
				fprintf(stderr,"LD reg, (expr) error\n");
				err[fflag]++;
			}
			else emit(3, 072, $4, $4 >> 8);
		}
|
	LD '(' RP ')' ',' ACC
		{ emit(1, 2 + $3->i_value); }
|
	LD parenexpr ',' ACC
		{ emit(3, 062, $2, $2 >> 8); }
|
	LD reg ',' MISCREG
		{
			if ($2 != 7) {
				fprintf(stderr,"LD reg, MISCREG error\n");
				err[fflag]++;
			}
			else emit(2, 0355, 0127 + $4->i_value);
		}
|
	LD MISCREG ',' ACC
		{ emit(2, 0355, 0107 + $2->i_value); }
|
	LD evenreg ',' lxexpression
		{ emit1(1 + ($2 & 060), $2, $4, 5); }
|
	LD evenreg ',' parenexpr
		{
			if (($2 & 060) == 040)
				emit1(052, $2, $4, 5);
			else
				emit(4, 0355, 0113 + $2, $4, $4 >> 8);
		}
|
	LD parenexpr ',' evenreg
		{
			if (($4 & 060) == 040)
				emit1(042, $4, $2, 5);
			else
				emit(4, 0355, 0103 + $4, $2, $2 >> 8);
		}
|
	LD evenreg ',' mar
		{
			if ($2 != 060) {
				fprintf(stderr,"LD evenreg error\n");
				err[fflag]++;
			}
			else
				emit1(0371, $4, 0, 1);
		}
|
	EX RP ',' HL
		{
			if ($2->i_value != 020) {
				fprintf(stderr,"EX RP, HL error\n");
				err[fflag]++;
			}
			else
				emit(1, 0353);
		}
|
	EX AF ',' AF setqf '\'' clrqf
		{ emit(1, 010); }
|
	EX '(' SP ')' ',' mar
		{ emit1(0343, $6, 0, 1); }
|
	IN realreg ',' parenexpr
		{
			if ($2 != 7) {
				fprintf(stderr,"IN reg, (expr) error\n");
				err[fflag]++;
			}
			else	{
				if ($4 < 0 || $4 > 255)
					err[vflag]++;
				emit(2, $1->i_value, $4);
			}
		}
|
	IN realreg ',' '(' C ')'
		{ emit(2, 0355, 0100 + ($2 << 3)); }
|
	IN F ',' '(' C ')'
		{ emit(2, 0355, 0160); }
|
	OUT parenexpr ',' ACC
		{
			if ($2 < 0 || $2 > 255)
				err[vflag]++;
			emit(2, $1->i_value, $2);
		}
|
	OUT '(' C ')' ',' realreg
		{ emit(2, 0355, 0101 + ($6 << 3)); }
|
	IM expression
		{
			if ($2 > 2 || $2 < 0)
				err[vflag]++;
			else
				emit(2, $1->i_value >> 8, $1->i_value + (($2 + ($2 > 0)) << 3));
		}
|
	PHASE expression
		{
			if (phaseflag) {
				err[oflag]++;
			} else {
				phaseflag = 1;
				phdollar = dollarsign;
				dollarsign = $2;
				phbegin = dollarsign;
			}
		}
|
	DEPHASE
		{
			if (!phaseflag) {
				err[oflag]++;
			} else {
				phaseflag = 0;
				dollarsign = phdollar + dollarsign - phbegin;
			}
		}
|
	ORG expression
		{
			if (phaseflag) {
				err[oflag]++;
				dollarsign = phdollar + dollarsign - phbegin;
				phaseflag = 0;
			}
			if ($2-dollarsign) {
				flushbin();
				olddollar = $2;
				dollarsign = $2;
			}
		}
|
	DEFB db.list
|
	DEFW dw.list
|
	ENDM
;


parm.list:
|
	parm.element
|
	parm.list ',' parm.element
;


parm.element:
	UNDECLARED
		{
			$1->i_token = MPARM;
			if (parm_number >= PARMMAX)
				error("Too many parameters");
			$1->i_value = parm_number++;
		}
;


arg.list:
	/* empty */
|
	arg.element
|
	arg.list ',' arg.element
;


arg.element:
	ARG
		{
			cp = malloc(strlen(tempbuf)+1);
			est2[parm_number++] = cp;
			strcpy(cp, tempbuf);
		}
;
reg:
	realreg
|
	mem
;
realreg:
	REGNAME
		{
			$$ = $1->i_value;
		}
|
	ACC
		{
			$$ = $1->i_value;
		}
|
	C
		{
			$$ = $1->i_value;
		}
;
mem:
	'(' HL ')'
		{
			$$ = 6;
		}
|
	'(' INDEX expression ')'
		{
			disp = $3;
			$$ = ($2->i_value & 0177400) | 6;
		}
|
	'(' INDEX ')'
		{
			disp = 0;
			$$ = ($2->i_value & 0177400) | 6;
		}
;
evenreg:
	bcdesp
|
	mar
;
pushable:
	RP
		{
			$$ = $1->i_value;
		}
|
	AF
		{
			$$ = $1->i_value;
		}
|
	mar
;
bcdesp:
	RP
		{
			$$ = $1->i_value;
		}
|
	SP
		{
			$$ = $1->i_value;
		}
;
bcdehlsp:
	bcdesp
|
	HL
		{
			$$ = $1->i_value;
		}
;
mar:
	HL
		{
			$$ = $1->i_value;
		}
|
	INDEX
		{
			$$ = $1->i_value;
		}
;
condition:
	spcondition
|
	COND
		{
			$$ = $1->i_value;
		}
;
spcondition:
	SPCOND
		{
			$$ = $1->i_value;
		}
|
	C
		{	$$ = 030;	}
;
db.list:
	db.list.element
|
	db.list ',' db.list.element
;
db.list.element:
	TWOCHAR
		{
			emit(2, $1, $1>>8);
		}
|
	STRING
		{
			cp = $1;
			while (*cp != '\0')
				emit(1,*cp++);
		}
|
	expression
		{
			if ($1 < -128 || $1 > 255)
					err[vflag]++;
			emit(1, $1 & 0377);
		}
;


dw.list:
	dw.list.element
|
	dw.list ',' dw.list.element
;


dw.list.element:
	expression
		{
			emit(2, $1, $1>>8);
		}
;



lxexpression:
	expression
|
	TWOCHAR
;

parenexpr:
	'(' expression ')'
		{	$$ = $2;	}
;

expression:
	error
		{
			err[eflag]++;
			$$ = 0;
		}
|
	LABEL
		{	$$ = $1->i_value; $1->i_uses++ ;	}
|
	NUMBER
|
	ONECHAR
|
	EQUATED
		{	$$ = $1->i_value;	}
|
	WASEQUATED
		{	$$ = $1->i_value;	}
|
	DEFLED
		{	$$ = $1->i_value;	}
|
	'$'
		{	$$ = dollarsign;	}
|
	UNDECLARED
		{
			err[uflag]++;
			$$ = 0;
		}
|
	MULTDEF
		{	$$ = $1->i_value;	}
|
	expression '+' expression
		{	$$ = $1 + $3;	}
|
	expression '-' expression
		{	$$ = $1 - $3;	}
|
	expression '/' expression
		{	$$ = $1 / $3;	}
|
	expression '*' expression
		{	$$ = $1 * $3;	}
|
	expression MOD expression
		{	$$ = $1 % $3;	}
|
	expression '&' expression
		{	$$ = $1 & $3;	}
|
	expression '|' expression
		{	$$ = $1 | $3;	}
|
	expression '^' expression
		{	$$ = $1 ^ $3;	}
|
	expression SHL expression
		{	$$ = $1 << $3;	}
|
	expression SHR expression
		{	$$ = (($1 >> 1) & 077777) >> ($3 - 1);	}
|
	'[' expression ']'
		{	$$ = $2;	}
|
	NOT expression
		{	$$ = ~$2;	}
|
	'+' expression %prec UNARY
		{	$$ = $2;	}
|
	'-' expression %prec UNARY
		{	$$ = -$2;	}
;

symbol:
	UNDECLARED
|
	LABEL
|
	MULTDEF
|
	EQUATED
|
	WASEQUATED
|
	DEFLED
;


al:
	{
		if (expptr >= MAXEXP)
			error("Macro expansion level");
		est2 = (char **) malloc((PARMMAX +4) * sizeof(char *));
		expstack[expptr] = (char *)est2 ;
		for (i=0; i<PARMMAX; i++)
			est2[i] = 0;
		arg_flag++;
	}
;


arg_on:
	{	arg_flag++;	}
;

arg_off:
		{	arg_flag = 0;	}
;

setqf:
		{	quoteflag++;	}
;

clrqf:
		{	quoteflag = 0;	}

;

%%
/*extern int	yylval;*/

#define F_END	0
#define OTHER	1
#define SPACE	2
#define DIGIT	3
#define LETTER	4
#define STARTER 5


/*
 *  This is the table of character classes.  It is used by the lexical
 *  analyser. (yylex())
 */
char	charclass[] = {
	F_END,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,
	OTHER,	SPACE,	OTHER,	OTHER,	OTHER,	SPACE,	OTHER,	OTHER,
	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,
	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,
	SPACE,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,
	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,
	DIGIT,	DIGIT,	DIGIT,	DIGIT,	DIGIT,	DIGIT,	DIGIT,	DIGIT,
	DIGIT,	DIGIT,	OTHER,	OTHER,	OTHER,	OTHER,	OTHER,	STARTER,
	STARTER,LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
	LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
	LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
	LETTER, LETTER, LETTER, OTHER,	OTHER,	OTHER,	OTHER,	LETTER,
	OTHER,	LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
	LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
	LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
	LETTER, LETTER, LETTER, OTHER,	OTHER,	OTHER,	OTHER,	OTHER,
};


/*
 *  the following table tells which characters are parts of numbers.
 *  The entry is non-zero for characters which can be parts of numbers.
 */
char	numpart[] = {
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	'0',	'1',	'2',	'3',	'4',	'5',	'6',	'7',
	'8',	'9',	0,	0,	0,	0,	0,	0,
	0,	'A',	'B',	'C',	'D',	'E',	'F',	0,
	'H',	0,	0,	0,	0,	0,	0,	'O',
	0,	'Q',	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0,	'a',	'b',	'c',	'd',	'e',	'f',	0,
	'h',	0,	0,	0,	0,	0,	0,	'o',
	0,	'q',	0,	0,	0,	0,	0,	0,
	0,	0,	0,	0,	0,	0,	0,	0,
	0};




/*
 *  the following table is a list of assembler mnemonics;
 *  for each mnemonic the associated machine-code bit pattern
 *  and symbol type are given.
 */
struct	item	keytab[] = {
	"a",	7,	ACC,	0,
	"adc",	1,	ARITHC,	0,
	"add",	0,	ADD,	0,
	"af",	060,	AF,	0,
	"and",	4,	LOGICAL,	0,
	"ascii",0,	DEFB,	0,
	"b",	0,	REGNAME,	0,
	"bc",	0,	RP,	0,
	"bit",	0145500,BIT,	0,
	"block",0,	DEFS,	0,
	"byte",	0,	DEFB,	0,
	"c",	1,	C,	0,
	"call", 0315,	CALL,	0,
	"ccf",	077,	NOOPERAND,	0,
	"cmp",	7,	LOGICAL,	0,		/* -cdk */
	"cp",	7,	LOGICAL,	0,
	"cpd",	0166651,NOOPERAND,	0,
	"cpdr",	0166671,NOOPERAND,	0,
	"cpi",	0166641,NOOPERAND,	0,
	"cpir",	0166661,NOOPERAND,	0,
	"cpl",	057,	NOOPERAND,	0,
	"d",	2,	REGNAME,	0,
	"daa",	0047,	NOOPERAND,	0,
	"de",	020,	RP,	0,
	"dec",	1,	INCDEC,	0,
	"defb",	0,	DEFB,	0,
	"defl",0,	DEFL,	0,
	"defs",	0,	DEFS,	0,
	"defw",	0,	DEFW,	0,
	"dephase",	0,	DEPHASE,	0,
	"di",	0363,	NOOPERAND,	0,
	"djnz",	020,	DJNZ,	0,
	"e",	3,	REGNAME,	0,
	"ei",	0373,	NOOPERAND,	0,
	"eject",1,	LIST,	0,
	"elist",3,	LIST,	0,
	"end",	0,	END,	0,
	"endif",0,	ENDIF,	0,
	"endm", 0,	ENDM,	0,
	"equ",	0,	EQU,	0,
	"ex",	0,	EX,	0,
	"exx",	0331,	NOOPERAND,	0,
	"f",	0,	F,	0,
	"flist",4,	LIST,	0,
	"glist",5,	LIST,	0,
	"h",	4,	REGNAME,	0,
	"halt",	0166,	NOOPERAND,	0,
	"hl",	040,	HL,	0,
	"i",	0,	MISCREG,	0,
	"if",	0,	IF,	0,
	"im",	0166506,IM,	0,
	"in",	0333,	IN,	0,
	"inc",	0,	INCDEC,	0,
	"include", 3,	ARGPSEUDO,	0,
	"ind",	0166652,NOOPERAND,	0,
	"indr",	0166672,NOOPERAND,	0,
	"ini",	0166642,NOOPERAND,	0,
	"inir",	0166662,NOOPERAND,	0,
	"ix",	0156440,INDEX,	0,
	"iy",	0176440,INDEX,	0,
	"jmp",	0303,	JP,	0,		/* -cdk */
	"jp",	0303,	JP,	0,
	"jr",	040,	JR,	0,
	"l",	5,	REGNAME,	0,
	"ld",	0,	LD,	0,
	"ldd",	0166650,NOOPERAND,	0,
	"lddr",	0166670,NOOPERAND,	0,
	"ldi",	0166640,NOOPERAND,	0,
	"ldir",	0166660,NOOPERAND,	0,
	"list",	0,	LIST,	0,
	"m",	070,	COND,	0,
	"macro",0,	MACRO,	0,
	"max",	1,	MINMAX,	0,
	"min",	0,	MINMAX,	0,
	"mlist",6,	LIST,	0,
	"mod",	0,	MOD,	0,
	"nc",	020,	SPCOND,	0,
	"neg",	0166504,NOOPERAND,	0,
	"nolist",-1,	LIST,	0,
	"nop",	0,	NOOPERAND,	0,
	"not",	0,	NOT,	0,
	"nv",	040,	COND,	0,
	"nz",	0,	SPCOND,	0,
	"or",	6,	LOGICAL,	0,
	"org",	0,	ORG,	0,
	"otdr",0166673,NOOPERAND,	0,
	"otir",0166663,NOOPERAND,	0,
	"out",	0323,	OUT,	0,
	"outd",	0166653,NOOPERAND,	0,
	"outi",	0166643,NOOPERAND,	0,
	"p",	060,	COND,	0,
	"pe",	050,	COND,	0,
	"phase",	0,	PHASE,	0,
	"po",	040,	COND,	0,
	"pop",	0301,	PUSHPOP,	0,
	"push", 0305,	PUSHPOP,	0,
	"r",	010,	MISCREG,	0,
	"res",	0145600,BIT,	0,
	"ret",	0311,	RET,	0,
	"reti",	0166515,NOOPERAND,	0,
	"retn",	0166505,NOOPERAND,	0,
	"rl",	2,	SHIFT,	0,
	"rla",	027,	NOOPERAND,	0,
	"rlc",	0,	SHIFT,	0,
	"rlca",	07,	NOOPERAND,	0,
	"rld",	0166557,NOOPERAND,	0,
	"rr",	3,	SHIFT,	0,
	"rra",	037,	NOOPERAND,	0,
	"rrc",	1,	SHIFT,	0,
	"rrca",	017,	NOOPERAND,	0,
	"rrd",	0166547,NOOPERAND,	0,
	"rst",	0307,	RST,	0,
	"rsym",	1,	ARGPSEUDO,	0,
	"sbc",	3,	ARITHC,	0,
	"scf",	067,	NOOPERAND,	0,
	"set",	0145700,BIT,	0,
	"shl",	0,	SHL,	0,
	"shr",	0,	SHR,	0,
	"sla",	4,	SHIFT,	0,
	"sp",	060,	SP,	0,
	"space",2,	LIST,	0,
	"sra",	5,	SHIFT,	0,
	"srl",	7,	SHIFT,	0,
	"sub",	2,	LOGICAL,	0,
	"title",0,	ARGPSEUDO,	0,
	"v",	050,	COND,	0,
	"word",	0,	DEFW,	0,
	"wsym",	2,	ARGPSEUDO,	0,
	"xor",	5,	LOGICAL,	0,
	"z",	010,	SPCOND,	0,
};

/*
 *  user-defined items are tabulated in the following table.
 */

struct item	itemtab[ITEMTABLESIZE];
struct item	*itemmax = &itemtab[ITEMTABLESIZE];





/*
 *  lexical analyser, called by yyparse.
 */
yylex()
{
	register char	c;
	register char *p;
	register int	radix;
	int  limit;

	if (arg_flag)
		return(getarg());
loop switch(charclass[c = nextchar()]) {
	case F_END:
		if (expptr) {
			popsi();
			continue;
		} else return(0);

	case SPACE:
		break;
	case LETTER:
	case STARTER:
		p = tempbuf;
		do {
			if (p >= tempmax)
				error(symlong);
			*p++ = (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
			while	((c = nextchar()) == '$')
				;
		} while	(charclass[c]==LETTER || charclass[c]==DIGIT);
		if (p - tempbuf > MAXSYMBOLSIZE)
			p = tempbuf + MAXSYMBOLSIZE;
		*p++ = '\0';
		peekc = c;
		return(tokenofitem(UNDECLARED));
	case DIGIT:
		if (*ifptr) return(skipline(c));
		p = tempbuf;
		do	{
			if (p >= tempmax)
				error(symlong);
			*p++ = (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
			while	((c = nextchar()) == '$');
			}
			while(numpart[c]);
		peekc = c;
		*p-- = '\0';
		switch(*p)	{
			case 'o':
			case 'q':
				radix = 8;
				limit = 020000;
				*p = '\0';
				break;
			case 'd':
				radix = 10;
				limit = 3276;
				*p = '\0';
				break;
			case 'h':
				radix = 16;
				limit = 010000;
				*p = '\0';
				break;
			case 'b':
				radix = 2;
				limit = 077777;
				*p = '\0';
				break;
			default:
				radix = 10;
				limit = 3276;
				p++;
				break;
			}

		/*
		 *  tempbuf now points to the number, null terminated
		 *  with radix 'radix'.
		 */
		yylval.ival = 0;
		p = tempbuf;
		do	{
			c = *p - (*p > '9' ? ('a' - 10) : '0');
			if (c >= radix)
				{
				err[iflag]++;
				yylval.ival = 0;
				break;
				}
			if (yylval.ival < limit ||
				(radix == 10 && yylval.ival == 3276 && c < 8) ||
				(radix == 2 && yylval.ival == limit))
				yylval.ival = yylval.ival * radix + c;
			else {
				err[vflag]++;
				yylval.ival = 0;
				break;
				}
			}
			while(*++p != '\0');
		return(NUMBER);
	default:
		if (*ifptr)
			return(skipline(c));
		switch(c) {
		case ';':
			return(skipline(c));
		case '\'':
			if (quoteflag) return('\'');
			p = tempbuf;
			p[1] = 0;
			do	switch(c = nextchar())	{
			case '\0':
			case '\n':
				err[bflag]++;
				goto retstring;
			case '\'':
				if ((c = nextchar()) != '\'') {
				retstring:
					peekc = c;
					*p = '\0';
					if ((p-tempbuf) >2) {
						yylval.cval = tempbuf;
						return(STRING);
					} else if (p-tempbuf == 2)	{
						p = tempbuf;
						yylval.ival = *p++ ;
						yylval.ival |= *p<<8;
						return(TWOCHAR);
					} else	{
						p = tempbuf;
						yylval.ival = *p++;
						return(ONECHAR);
					}
				}
			default:
				*p++ = c;
			} while (p < tempmax);
			/*
			 *  if we break out here, our string is longer than
			 *  our input line
			 */
			error("string buffer overflow");
		default:
			return(c);
		}
	}
}

/*
 *  return the token associated with the string pointed to by
 *  tempbuf.  if no token is associated with the string, associate
 *  deftoken with the string and return deftoken.
 *  in either case, cause yylval to point to the relevant
 *  symbol table entry.
 */

tokenofitem(deftoken)
int	deftoken;
{
	register  char *p;
	register struct item *	ip;
	register  i;
	int  r, l, u, hash;


#ifdef T_DEBUG
	fputs("'tokenofitem entry'	", stderr) ;
	fputs(tempbuf, stderr) ;
#endif
	/*
	 *  binary search
	 */
	l = 0;
	u = (sizeof keytab/sizeof keytab[0])-1;
	while (l <= u) {
		i = (l+u)/2;
		ip = &keytab[i];
		if ((r = strcmp(tempbuf, ip->i_string)) == 0)
			goto found;
		if (r < 0)
			u = i-1;
		else
			l = i+1;
	}

	/*
	 *  hash into item table
	 */
	hash = 0;
	p = tempbuf;
	while (*p) hash += *p++;
	hash %= ITEMTABLESIZE;
	ip = &itemtab[hash];

	loop {
		if (ip->i_token == 0)
			break;
		if (strcmp(tempbuf, ip->i_string) == 0)
			goto found;
		if (++ip >= itemmax)
			ip = itemtab;
	}

	if (!deftoken) {
		i = 0 ;
		goto token_done ;
	}
	if (++nitems > ITEMTABLESIZE-20)
		error("item table overflow");
	ip->i_string = malloc(strlen(tempbuf)+1);
	ip->i_token = deftoken;
	ip->i_uses = 0 ;
	strcpy(ip->i_string, tempbuf);

found:
	if (*ifptr) {
		if (ip->i_token == ENDIF) {
			i = ENDIF ;
			goto token_done ;
		}
		if (ip->i_token == IF) {
			if (ifptr >= ifstmax)
				error("Too many ifs");
			else *++ifptr = 1;
		}
		i = skipline(' ');
		goto token_done ;
	}
	yylval.itemptr = ip;
	i = ip->i_token;
token_done:
#ifdef T_DEBUG
	fputs("\t'tokenofitem exit'\n", stderr) ;
#endif
	return(i) ;
}


/*
 *  interchange two entries in the item table -- used by qsort
 */
interchange(i, j)
{
	register  struct item *fp, *tp;
	struct item temp;

	fp = &itemtab[i];
	tp = &itemtab[j];
	temp.i_string = fp->i_string;
	temp.i_value = fp->i_value;
	temp.i_token = fp->i_token;
	temp.i_uses = fp->i_uses;

	fp->i_string = tp->i_string;
	fp->i_value = tp->i_value;
	fp->i_token = tp->i_token;
	fp->i_uses = tp->i_uses;

	tp->i_string = temp.i_string;
	tp->i_value = temp.i_value;
	tp->i_token = temp.i_token;
	tp->i_uses = temp.i_uses;
}



/*
 *  quick sort -- used by putsymtab to sort the symbol table
 */
qsort(m, n)
{
	register  i, j;

	if (m < n) {
		i = m;
		j = n+1;
		loop {
			do i++; while(strcmp(itemtab[i].i_string,
					itemtab[m].i_string) < 0);
			do j--; while(strcmp(itemtab[j].i_string,
					itemtab[m].i_string) > 0);
			if (i < j) interchange(i, j); else break;
		}
		interchange(m, j);
		qsort(m, j-1);
		qsort(j+1, n);
	}
}



/*
 *  get the next character
 */
nextchar()
{
	register int c, ch;
	static  char  *earg;
	char *getlocal();

	if (peekc != -1) {
		c = peekc;
		peekc = -1;
		return(c);
	}

start:
	if (earg) {
		if (*earg)
			return(addtoline(*earg++));
		earg = 0;
	}

	if (expptr) {
		if ((ch = getm()) == '\1') {	/*  expand argument  */
			ch = getm() - 'A';
			if (ch >= 0 && ch < PARMMAX && est[ch])
				earg = est[ch];
			goto start;
		}
		if (ch == '\2') {	/*  local symbol  */
			ch = getm() - 'A';
			if (ch >= 0 && ch < PARMMAX && est[ch]) {
				earg = est[ch];
				goto start;
			}
			earg = getlocal(ch, (int)est[TEMPNUM]);
			goto start;
		}

		return(addtoline(ch));
	}
	ch = getc(now_file) ;
	/* if EOF, check for include file */
	if (ch == EOF) {
		while (ch == EOF && now_in) {
			fclose(fin[now_in]) ;
			free(src_name[now_in]) ;
			now_file = fin[--now_in] ;
			ch = getc(now_file) ;
		}
		if (linein[now_in] < 0) {
			lstoff = 1 ;
			linein[now_in] = -linein[now_in] ;
		} else {
			lstoff = 0 ;
		}
		if (pass2 && iflist()) {
			lineout() ;
			fprintf(fout, "**** %s ****\n", src_name[now_in]) ;
		}
	}
	if (ch == '\n')
		linein[now_in]++ ;

	return(addtoline(ch)) ;
}


/*
 *  skip to rest of the line -- comments and if skipped lines
 */
skipline(ac)
{
	register  c;

	c = ac;
	while (c != '\n' && c != '\0')
		c = nextchar();
	return('\n');
}



main(argc, argv)
char  **argv;
{
	register  struct item *ip;
	register  i;
	int  files;
#ifdef DBUG
	extern  yydebug;
#endif

	fout = stdout ;
	fin[0] = stdin ;
	now_file = stdin ;
	files = 0;

	for (i=1; i<argc; i++) {
		if (*argv[i] == '-') while (*++argv[i]) switch(*argv[i]) {

		case 'b':	/*  no binary  */
			bopt = 0;
			continue;

#ifdef DBUG
		case 'd':	/*  debug  */
			yydebug++;
			continue;
#endif

		case 'e':	/*  error list only  */
			eopt = 0;
			edef = 0;
			continue;

		case 'f':	/*  print if skipped lines  */
			fopt++;
			fdef++;
			continue;

		case 'g':	/*  do not list extra code  */
			gopt = 0;
			gdef = 0;
			continue;

		case 'i':	/* do not list include files */
			iopt = 1 ;
			continue ;

		case 'l':	/*  no list  */
			lopt++;
			continue;

		case 'L':	/*  force listing of everything */
			lston++;
			continue;

		case 'm':	/*  print macro expansions  */
			mdef++;
			mopt++;
			continue;

		case 'n':	/*  put line numbers off */
			nopt-- ;
			continue;

		case 'o':	/*  list to standard output  */
			oopt++;
			continue;

		case 'p':	/*  put out four \n's for eject */
			popt-- ;
			continue;

		case 's':	/*  don't produce a symbol list  */
			sopt++;
			continue;

		case 't':
			topt = 0;
			continue;

		default:	/*  error  */
			error("Unknown option");

		} else if (files++ == 0) {
			sourcef = argv[i];
			strcpy(src, sourcef);
			suffix(src,".z");
			if ((now_file = fopen(src, "r")) == NULL)
				error("Cannot open source file");
			now_in = 0 ;
			fin[now_in] = now_file ;
			src_name[now_in] = src ;
		} else if (files)
			error("Too many arguments");
	}


	if (files == 0)
		error("No source file");
	strcpy(bin, sourcef);
	suffix(bin,".hex");
	if (bopt)
#ifdef MSDOS
		if (( fbuf = fopen(bin, "wb")) == NULL)
#else
		if (( fbuf = fopen(bin, "w")) == NULL)
#endif
			error("Cannot create binary file");
	if (!lopt && !oopt) {
		strcpy(listf, sourcef);
		suffix(listf,".lst");
		if ((fout = fopen(listf, "w")) == NULL)
			error("Cannot create list file");
	} else
		fout = stdout ;
	strcpy(mtmp, sourcef);
	suffix(mtmp,".tmp");
#ifdef MSDOS
	mfile = mfopen(mtmp,"w+b") ;
#else
	mfile = mfopen(mtmp,"w+") ;
#endif
	if (mfile == NULL) {
		error("Cannot create temp file");
	}
	/*unlink(mtmp);*/

	/*
	 *  get the time
	 */
	time(&now);
	timp = ctime(&now);
	timp[16] = 0;
	timp[24] = 0;

	title = sourcef;
	/*
	 * pass 1
	 */
#ifdef DEBUG
	fputs("DEBUG-pass 1\n", stderr) ;
#endif
	setvars();
	yyparse();
	pass2++;
	ip = &itemtab[-1];
	while (++ip < itemmax) {
		/* reset use count */
		ip->i_uses = 0 ;

		/* set macro names, equated and defined names */
		switch	(ip->i_token) {
		case MNAME:
			ip->i_token = OLDMNAME;
			break;

		case EQUATED:
			ip->i_token = WASEQUATED;
			break;

		case DEFLED:
			ip->i_token = UNDECLARED;
			break;
		}
	}
	setvars();
	fseek(now_file, (long)0, 0);

#ifdef DEBUG
	fputs("DEBUG- pass 2\n", stderr) ;
#endif
	yyparse();


	if (bopt) {
		flushbin();
		putc(':', fbuf);
		if (xeq_flag) {
			puthex(0, fbuf);
			puthex(xeq >> 8, fbuf);
			puthex(xeq, fbuf);
			puthex(1, fbuf);
			puthex(255-(xeq >> 8)-xeq, fbuf);
		} else
			for	(i = 0; i < 10; i++)
				putc('0', fbuf);
		putc('\n', fbuf);
		fflush(fbuf);
	}

	if (!lopt)
		fflush(fout);
	if (writesyms)
		outsymtab(writesyms);
	if (eopt)
		erreport();
	if (!lopt && !sopt)
		putsymtab();
	if (!lopt) {
		eject();
		fflush(fout);
	}
	exit(0);
}


/*
 *  set some data values before each pass
 */
setvars()
{
	register  i;

	peekc = -1;
	linein[now_in] = linecnt = 0;
	exp_number = 0;
	emitptr = emitbuf;
	lineptr = linebuf;
	ifptr = ifstack;
	expifp = expif;
	*ifptr = 0;
	dollarsign = 0;
	olddollar = 0;
	phaseflag = 0;
	for (i=0; i<FLAGS; i++) err[i] = 0;
}



/*
 *  print out an error message and die
 */
error(as)
char *as;
{

	*linemax = 0;
	fprintf(fout, "%s\n", linebuf);
	fflush(fout);
	fprintf(stderr, "%s\n", as) ;
	exit(1);
}



/*
 *  output the symbol table
 */
putsymtab()
{
	register  struct item *tp, *fp;
	int  i, j, k, t, rows;
	char c, c1 ;

	if (!nitems)
		return;

	/* compact the table so unused and UNDECLARED entries are removed */
	tp = &itemtab[-1];
	for (fp = itemtab; fp<itemmax; fp++) {
		if (fp->i_token == UNDECLARED) {
			nitems--;
			continue;
		}
		if (fp->i_token == 0)
			continue;
		tp++;
		if (tp != fp) {
			tp->i_string = fp->i_string;
			tp->i_value = fp->i_value;
			tp->i_token = fp->i_token;
			tp->i_uses = fp->i_uses ;
		}
	}

	tp++;
	tp->i_string = "{";

	/*  sort the table */
	qsort(0, nitems-1);

	title = "**  Symbol Table  **";

	rows = (nitems+3) / 4;
	if (rows+5+line > 60)
		eject();
	lineout();
	fprintf(fout,"\n\n\nSymbol Table:\n\n") ;
	line += 4;

	for (i=0; i<rows; i++) {
		for(j=0; j<4; j++) {
			k = rows*j+i;
			if (k < nitems) {
				tp = &itemtab[k];
				t = tp->i_token;
				c = ' ' ;
				if (t == EQUATED || t == DEFLED)
					c = '=' ;
				if (tp->i_uses == 0)
					c1 = '+' ;
				else
					c1 = ' ' ;
				fprintf(fout, "%-15s%c%4x%c    ",
					tp->i_string, c, tp->i_value & 0xffff, c1);
			}
		}
		lineout();
		putc('\n', fout);
	}
}




/*
 *  put out error report
 */
erreport()
{
	register i, numerr;

	if (line > 50) eject();
	lineout();
	numerr = 0;
	for (i=0; i<FLAGS; i++) numerr += keeperr[i];
	if (numerr) {
		fputs("\n\n\nError report:\n\n", fout);
		fprintf(fout, "%6d errors\n", numerr);
		line += 5;
	} else {
		fputs("\n\n\nStatistics:\n", fout);
		line += 3;
	}

	for (i=0; i<FLAGS; i++)
		if (keeperr[i]) {
			lineout();
			fprintf(fout, "%6d %c -- %s error\n",
				keeperr[i], errlet[i], errname[i]);
		}

	if (line > 55) eject();
	lineout();
	fprintf(fout, "\n%6d\tsymbols\n", nitems);
	fprintf(fout, "%6d\tbytes\n", nbytes);
	line += 2;
	if (mfptr) {
		if (line > 53) eject();
		lineout();
		fprintf(fout, "\n%6d\tmacro calls\n", exp_number);
		fprintf(fout, "%6d\tmacro bytes\n", mfptr);
		fprintf(fout, "%6d\tinvented symbols\n", invented/2);
		line += 3;
	}
}


/*
 *  lexical analyser for macro definition
 */
mlex()
{
	register  char  *p;
	register  c;
	int  t;

	/*
	 *  move text onto macro file, changing formal parameters
	 */
#ifdef	M_DEBUG
	fprintf(stderr,"enter 'mlex'\t") ;
#endif
	inmlex++;

	c = nextchar();
loop {
	switch(charclass[c]) {

	case DIGIT:
		while (numpart[c]) {
			putm(c);
			c = nextchar();
		}
		continue;

	case STARTER:
	case LETTER:
		t = 0;
		p = tempbuf+MAXSYMBOLSIZE+2;
		do {
			if (p >= tempmax)
				error(symlong);
			*p++ = c;
			if (t < MAXSYMBOLSIZE)
				tempbuf[t++] = (c >= 'A' && c <= 'Z')  ?
					c+'a'-'A' : c;
			c = nextchar();
		} while (charclass[c]==LETTER || charclass[c]==DIGIT);

		tempbuf[t] = 0;
		*p++ = '\0';
		p = tempbuf+MAXSYMBOLSIZE+2;
		t = tokenofitem(0);
		if (t != MPARM) while (*p) putm(*p++);
		else {
			if (*(yylval.itemptr->i_string) == '?') putm('\2');
			else putm('\1');
			putm(yylval.itemptr->i_value + 'A');
		}
		if (t == ENDM) goto done;
		continue;

	case F_END:
		if (expptr) {
			popsi();
			c = nextchar();
			continue;
		}

		goto done;

	default:
		if (c == '\n') {
			linecnt++;
		}
		if (c != '\1') putm(c);
		c = nextchar();
	}
}

	/*
	 *  finish off the file entry
	 */
done:
	while(c != EOF && c != '\n' && c != '\0') c = nextchar();
	linecnt++;
	putm('\n');
	putm('\n');
	putm(0);

	for (c=0; c<ITEMTABLESIZE; c++)
		if (itemtab[c].i_token == MPARM) {
			itemtab[c].i_token = UNDECLARED;
		}
	inmlex = 0;
#ifdef	M_DEBUG
	fprintf(stderr,"exit 'mlex'\n") ;
#endif
}



/*
 *  lexical analyser for the arguments of a macro call
 */
getarg()
{
	register int c;
	register char *p;
	static int comma;

	*tempbuf = 0;
	yylval.cval = tempbuf;
	while(charclass[c = nextchar()] == SPACE);

	switch(c) {

	case '\0':
		popsi();
	case '\n':
	case ';':
		comma = 0;
		return(skipline(c));

	case ',':
		if (comma) {
			comma = 0;
			return(',');
		}
		else {
			comma++;
			return(ARG);
		}

	case '\'':
		p = tempbuf;
		do switch (c = nextchar()) {
			case '\0':
			case '\n':
				peekc = c;
				*p = 0;
				err[bflag]++;
				return(ARG);
			case '\'':
				if ((c = nextchar()) != '\'') {
					peekc = c;
					*p = '\0';
					comma++;
					return(ARG);
				}
			default:
				*p++ = c;
		} while (p < tempmax);
		error(symlong);

	default:  /* unquoted string */
		p = tempbuf;
		peekc = c;
		do switch(c = nextchar()) {
			case '\0':
			case '\n':
			case '\t':
			case ' ':
			case ',':
				peekc = c;
				*p = '\0';
				comma++;
				return(ARG);
			default:
				*p++ = c;
		} while (p < tempmax);
	}
}





/*
 *  add a suffix to a string
 */
suffix(str,suff)
char *str,*suff;
{
	while(*str != '\0' && *str != '.')
		*str++;
	strcpy(str, suff);
}




/*
 *  put out a byte to the macro file, keeping the offset
 */
putm(c)
char c ;
{
	mfptr++;
	mfputc(c,mfile) ;
}



/*
 *  get a byte from the macro file
 */
getm()
{
	int ch;

	floc++;
	ch = mfgetc(mfile) ;
	if (ch == EOF) {
		ch = 0;
		fprintf(stderr,"bad macro read\n") ;
	}
	return(ch);
}



/*
 *  pop standard input
 */
popsi()
{
	register  i;

	for (i=0; i<PARMMAX; i++) {
		if (est[i]) free(est[i]);
	}
	floc = est[FLOC];
	free(est);
	expptr--;
	est = expptr ? (char **) expstack[expptr-1] : (char **) 0;
	mfseek(mfile, (long)floc, 0);
	if (lineptr > linebuf) lineptr--;
}



/*
 *  return a unique name for a local symbol
 *  c is the parameter number, n is the macro number.
 */

char *
getlocal(c, n)
int c,n;
{
static char local_label[10];
	invented++;
	if (c >= 26)
		c += 'a' - '0';
	sprintf(local_label, "?%c%04d", c+'a', n) ;
	return(local_label);
}



/*
 *  read in a symbol table
 */
insymtab(name)
char *name;
{
	register struct stab *t;
	int  s, i, sfile;

	t = (struct stab *) tempbuf;
#ifdef MSDOS
	if ((sfile = open(name, O_RDONLY | O_BINARY)) < 0)
#else
	if ((sfile = open(name, O_RDONLY)) < 0)
#endif
		return;
	read(sfile, (char *)t, sizeof *t);
	if (t->t_value != SYMMAJIC)
		return;

	s = t->t_token;
	for (i=0; i<s; i++) {
		read(sfile, (char *)t, sizeof *t);
		if (tokenofitem(UNDECLARED) != UNDECLARED)
			continue;
		yylval.itemptr->i_token = t->t_token;
		yylval.itemptr->i_value = t->t_value;
		if (t->t_token == MACRO)
			yylval.itemptr->i_value += mfptr;
	}

	while ((s = read(sfile, tempbuf, TEMPBUFSIZE)) > 0) {
		mfptr += s;
		mfwrite(tempbuf, 1, s, mfile) ;
	}
}



/*
 *  write out symbol table
 */
outsymtab(name)
char *name;
{
	register struct stab *t;
	register struct item *ip;
	int  i, sfile;

	t = (struct stab *) tempbuf;
	if ((sfile = creat(name, 0644)) < 0)
		return;
	for (ip=itemtab; ip<itemmax; ip++) {
		if (ip->i_token == UNDECLARED) {
			ip->i_token = 0;
			nitems--;
		}
	}

	copyname(title, (char *)t);
	t->t_value = SYMMAJIC;
	t->t_token = nitems;
	write(sfile, (char *)t, sizeof *t);

	for (ip=itemtab; ip<itemmax; ip++) {
		if (ip->i_token != 0) {
			t->t_token = ip->i_token;
			t->t_value = ip->i_value;
			copyname(ip->i_string, (char *)t);
			write(sfile, (char *)t, sizeof *t);
		}
	}

	mfseek(mfile, (long)0, 0);
	while((i = mfread(tempbuf, 1, TEMPBUFSIZE, mfile) ) > 0)
		write(sfile, tempbuf, i);
}



/*
 *  copy a name into the symbol file
 */
copyname(st1, st2)
char *st1, *st2;
{
	register  char  *s1, *s2;
	register  i;

	i = (MAXSYMBOLSIZE+2) & ~01;
	s1 = st1;
	s2 = st2;

	while(*s2++ = *s1++) i--;
	while(--i > 0) *s2++ = '\0';
}

/* get the next source file */
next_source(sp)
char *sp ;
{

	if(now_in == NEST_IN -1)
		error("Too many nested includes") ;
	if ((now_file = fopen(sp, "r")) == NULL) {
		char ebuf[100] ;
		sprintf(ebuf,"Can't open include file: %s", sp) ;
		error(ebuf) ;
	}
	if (pass2 && iflist()) {
		lineout() ;
		fprintf(fout, "**** %s ****\n",sp) ;
	}

	/* save the list control flag with the current line number */
	if (lstoff)
		linein[now_in] = - linein[now_in] ;

	/* no list if include files are turned off */
	lstoff |= iopt ;

	/* save the new file descriptor. */
	fin[++now_in] = now_file ;
	/* start with line 0 */
	linein[now_in] = 0 ;
	/* save away the file name */
	src_name[now_in] = malloc(strlen(sp)+1) ;
	strcpy(src_name[now_in],sp) ;
}