
#include    <stdio.h>
#include    <string.h>
#include    <stdlib.h>
#include    <sys/stat.h>
#include    <fcntl.h>

#include    "symbol.h"
#include    "config.h"

#ifdef MSDOS
#include    <io.h>
#else
#include    <unistd.h>
#endif


/* external functions */
enum symbols		GetSym(void);
int			DefineSymbol(char *identifier, long value, unsigned char symboltype);
int			ExprSigned8(int	listoffset);
int			ExprUnsigned8(int listoffset);
int			ExprAddress(int	listoffset);
int			ExprLong(int listoffset);
int			DefineDefSym(char *identifier, long value, unsigned char symtype, avltree ** root);
int			DEFSP(void);
char		       *AllocIdentifier(size_t len);
long			EvalPfixExpr(struct expr * pfixexpr);
long			GetConstant(char *evalerr);
void			Pass2info(struct expr *	expression, char constrange, long lfileptr);
void			ReportError(char *filename, short linenr, int errnum);
void			RemovePfixlist(struct expr * pfixexpr);
void			Z80pass1(void);
void			Skipline(void);
struct expr	       *ParseNumExpr(void);
struct sourcefile      *Newfile(struct sourcefile * curfile, char *fname);
struct sourcefile      *Prevfile(void);

/* local functions */
void			DeclModuleName(void);
void			Fetchfilename(void);
void			DefSym(void);

/* global variables */
extern FILE	       *z80asmfile, *listfile;
extern unsigned	char   *codeptr, *codearea;
extern char		ident[], stringconst[];
extern unsigned	short	PC;
extern enum symbols	sym;
extern enum flag	verbose, writeline, EOL;
extern struct modules  *modulehdr;
extern struct module   *CURRENTMODULE;
extern int		ASSEMBLE_ERROR;
extern int		sourcefile_open;



int			DEFSP(void)
{
	if (GetSym() ==	fullstop)
		if (GetSym() ==	name)
			switch (ident[0]) {
					case 'B':return	1;
				case 'W':
					return 2;
				case 'P':
					return 3;
				case 'L':
					return 4;
				default:
					return -1;
			}
		else {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
			return -1;
		}
	else {
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
		return -1;
	}
}


long			Parsevarsize(void)
{
	struct expr	       *postfixexpr;
	long			offset = 0, varsize, size_multiplier;

	if (strcmp(ident, "DS")	!= 0)
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 11);
	else {
		if ((varsize = DEFSP())	== -1)
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 10);
		else {
			GetSym();
			if ((postfixexpr = ParseNumExpr()) != NULL) {
				if (postfixexpr->rangetype & NOTEVALUABLE) {
					ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 2);
					RemovePfixlist(postfixexpr);
				} else {
					size_multiplier	= EvalPfixExpr(postfixexpr);
					RemovePfixlist(postfixexpr);
					if (size_multiplier > 0	&& size_multiplier < 16384U)
						offset = varsize * size_multiplier;
					else
						ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 4);
				}
			}
		}
	}

	return offset;
}


long			Parsedefvarsize(long offset)
{
	long			varoffset = 0;

	switch (sym) {
		case name:
			if (strcmp(ident, "DS")	!= 0) {
				DefineSymbol(ident, offset, 0);
				GetSym();
			}
			switch (sym) {
				case name:
					varoffset = Parsevarsize();
					break;
				case semicolon:
				case newline:
					break;
			}
			break;

		default:
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
	}

	return varoffset;
}


void			DEFVARS(void)
{
	struct expr	       *postfixexpr;
	long			offset;

	writeline = OFF;	/* DEFVARS definitions are not output'ed to listing file */

	GetSym();
	if ((postfixexpr = ParseNumExpr()) != NULL) {   /* expr. must not be stored in relocatable file */
		if (postfixexpr->rangetype & NOTEVALUABLE) {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 2);
			RemovePfixlist(postfixexpr);
			return;
		} else {
			offset = EvalPfixExpr(postfixexpr);	/* offset expression must not contain undefined	symbols	*/
			RemovePfixlist(postfixexpr);
		}
	} else
		return;		/* syntax error	- get next line	from file... */

	while (!feof(z80asmfile) && sym	!= lcurly) {
		Skipline();
		EOL = OFF;
		++CURRENTFILE->line;
		GetSym();
	}
	if (sym	== lcurly) {
		while (!feof(z80asmfile) && GetSym() !=	rcurly)	{
			if (EOL	== ON) {
				++CURRENTFILE->line;
				EOL = OFF;
			} else
				offset += Parsedefvarsize(offset);
		}
	}
}


void			DEFGROUP(void)
{
	struct expr	       *postfixexpr;
	long			constant, enumconst = 0;

	writeline = OFF;	/* DEFGROUP definitions	are not	output'ed to listing file */

	while (!feof(z80asmfile) && GetSym() !=	lcurly)	{
		Skipline();
		EOL = OFF;
		++CURRENTFILE->line;
	}

	if (sym	== lcurly) {
		while (!feof(z80asmfile) && sym	!= rcurly) {
			if (EOL	== ON) {
				++CURRENTFILE->line;
				EOL = OFF;
			} else {
				do {
					GetSym();
					switch (sym) {
						case rcurly:
						case semicolon:
						case newline:
							break;

						case name:
							strcpy(stringconst, ident);	/* remember name */
							if (GetSym() ==	assign)	{
								GetSym();
								if ((postfixexpr = ParseNumExpr()) != NULL) {
									if (postfixexpr->rangetype & NOTEVALUABLE)
										ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 2);
									else {
										constant = EvalPfixExpr(postfixexpr);
										enumconst = constant;
										DefineSymbol(stringconst, enumconst++, 0);
									}
									RemovePfixlist(postfixexpr);
								}
								GetSym();	/* prepare for next identifier */
							} else
								DefineSymbol(stringconst, enumconst++, 0);
							break;

						default:
							ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
							break;
					}
				}
				while (sym == comma);	/* get enum definitions	separated by comma in current line */
				Skipline();	/* ignore rest of line */
			}
		}
	}
}



void			DEFS()
{
	struct expr	       *postfixexpr;
	long			constant;

	GetSym();		/* get numerical expression */
	if ((postfixexpr = ParseNumExpr()) != NULL) {   /* expr. must not be stored in relocatable file */
		if (postfixexpr->rangetype & NOTEVALUABLE)
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 2);
		else {
			constant = EvalPfixExpr(postfixexpr);
			if (constant >=	0) {
				if ((codeptr - codearea	+ constant) <= MAXCODESIZE) {
					PC += constant;
					while (constant--)
						*codeptr++ = 0;
				} else {
					ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 12);
					return;
				}
			} else {
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 7);
				return;
			}

		}
		RemovePfixlist(postfixexpr);	/* remove linked list, expression evaluated */
	}
}


void			DefSym(void)
{
	do {
		if (GetSym() ==	name)
			DefineDefSym(ident, 1, 0, &CURRENTMODULE->localroot);
		else {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
			break;
		}
	}
	while (GetSym()	== comma);
}




void			DEFC(void)
{
	struct expr	       *postfixexpr;
	long			constant;

	do {
		if (GetSym() ==	name) {
			strcpy(stringconst, ident);	/* remember name */
			if (GetSym() ==	assign)	{
				GetSym();	/* get numerical expression */
				if ((postfixexpr = ParseNumExpr()) != NULL) {   /* expr. must not be stored in
										 * relocatable file */
					if (postfixexpr->rangetype & NOTEVALUABLE) {
						ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 2);

						break;
					} else {
						constant = EvalPfixExpr(postfixexpr);	/* DEFC	expression must	not
											 * contain undefined symbols */
						DefineSymbol(stringconst, constant, 0);
					}
					RemovePfixlist(postfixexpr);
				} else
					break;	/* syntax error	- get next line	from file... */
			} else {
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
				break;
			}
		} else {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
			break;
		}
	}
	while (sym == comma);	/* get all DEFC	definition separated by	comma */
}


void			ORG(void)
{
	struct expr	       *postfixexpr;
	long			constant;

	GetSym();		/* get numerical expression */
	if ((postfixexpr = ParseNumExpr()) != NULL) {
		if (postfixexpr->rangetype & NOTEVALUABLE)
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 2);
		else {
			constant = EvalPfixExpr(postfixexpr);	/* ORG expression must not contain undefined symbols */
			if (constant >=	0 && constant <= 65535U)
				CURRENTMODULE->origin =	constant;
			else
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 7);
		}
		RemovePfixlist(postfixexpr);
	}
}


void			DEFB(void)
{
	long			bytepos	= 0;

	do {
		GetSym();
		if (!ExprUnsigned8(bytepos))
			break;	/* syntax error	- get next line	from file... */
		++PC;		/* DEFB	allocated, update assembler PC */
		++bytepos;

		if (sym	== newline)
			break;
		else
			if (sym	!= comma) {
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
				break;
			}
	}
	while (sym == comma);	/* get all DEFB	definitions separated by comma */
}


void			DEFW(void)
{
	long			bytepos	= 0;

	do {
		GetSym();
		if (!ExprAddress(bytepos))
			break;	/* syntax error	- get next line	from file... */
		PC += 2;	/* DEFW	allocated, update assembler PC */
		bytepos	+= 2;

		if (sym	== newline)
			break;
		else
			if (sym	!= comma) {
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
				break;
			}
	}
	while (sym == comma);	/* get all DEFB	definitions separated by comma */
}



void			DEFL(void)
{
	long			bytepos	= 0;

	do {
		GetSym();
		if (!ExprLong(bytepos))
			break;	/* syntax error	- get next line	from file... */
		PC += 4;	/* DEFL	allocated, update assembler PC */
		bytepos	+= 4;

		if (sym	== newline)
			break;
		else
			if (sym	!= comma) {
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
				break;
			}
	}
	while (sym == comma);	/* get all DEFB	definitions separated by comma */
}



void			DEFM(void)
{
	long			constant, bytepos = 0;

	do {
		if (GetSym() ==	dquote)	{
			while ((constant = fgetc(z80asmfile)) != EOF) {
				if (constant !=	'\"') {
					*codeptr++ = constant;
					++bytepos;
					++PC;
				} else {
					GetSym();
					if (sym	!= strconq && sym != newline &&	sym != semicolon) {
						ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
						return;
					}
					break;	/* get out of loop */
				}
			}
		} else {
			if (!ExprUnsigned8(bytepos))
				break;	/* syntax error	- get next line	from file... */

			if (sym	!= strconq && sym != newline &&	sym != semicolon) {
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);	/* expression separator	not
											 * found */
				break;
			}
			++bytepos;
			++PC;
		}
	}
	while (sym != newline && sym !=	semicolon);
}



void			_INCLUDE(void)
{

	if (GetSym() ==	dquote)	{       /* fetch filename of include file */
		Fetchfilename();
		CURRENTFILE->filepointer = ftell(z80asmfile);	/* get file position of	current	source file */
		fclose(z80asmfile);	/* close current source	file */

		if ((z80asmfile	= fopen(ident, "r")) ==	NULL) { /* Open include file */
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 0);
			z80asmfile = fopen(CURRENTFILE->fname, "r");		/* re-open current source file */
			fseek(z80asmfile, CURRENTFILE->filepointer, SEEK_SET);	/* file	position to beginning of line
										 * following INCLUDE line */
			return;
		} else {
			sourcefile_open	= 1;
			CURRENTFILE = Newfile(CURRENTFILE, ident);	/* Allocate new	file into file information list	*/
			if (ASSEMBLE_ERROR == 3) return;		/* No room... */
			if (verbose) puts(CURRENTFILE->fname);		/* display name	of INCLUDE file	*/

			Z80pass1();					/* parse include file */
			CURRENTFILE = Prevfile();			/* Now get back	to current file... */
			switch (ASSEMBLE_ERROR)	{
				case 0:
				case 3:
				case 12:
					return;	/* Fatal errors, return	immediatly... */
			}
			sourcefile_open	= fclose(z80asmfile);
			if ((z80asmfile	= fopen(CURRENTFILE->fname, "r")) == NULL) {    /* re-open current source file */
				ReportError(NULL, 0, 0);
			} else {
				fseek(z80asmfile, CURRENTFILE->filepointer, 0);	/* file	position to beginning of */
				sourcefile_open	= 1;
			}
		}		/* line	following INCLUDE line	*/
	} else
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);

	sym = newline;
	writeline = OFF;	/* don't write current source line to listing file (empty line of INCLUDE file) */
}


void			BINARY(void)
{
	struct stat		filestatus;
	int			binfile;

	if (GetSym() ==	dquote)	{       /* fetch file name */
		Fetchfilename();
		if ((binfile = open(ident, O_RDONLY, 0)) != EOF) {
			fstat(binfile, &filestatus);
			if ((codeptr - codearea	+ filestatus.st_size) <= MAXCODESIZE) {
				if (read(binfile, codeptr, filestatus.st_size) == filestatus.st_size) {
					codeptr	+= filestatus.st_size;	/* codeptr updated */
					PC += filestatus.st_size;
					close(binfile);
				} else
					ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 15);
			} else
				ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 12);
			close(binfile);
		} else
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 0);
	} else
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 1);
}


void			Fetchfilename(void)
{
	int			l, c;
	char		       *stdpath;

	for (l = 0; (c = fgetc(z80asmfile)) != EOF; l++) {
		if (c == '\n')
			break;
		if (c != '"')
			ident[l] = c;
		else
			break;
	}
	ident[l] = '\0';					/* null-terminate file name */
	if (c != '\n') while (fgetc(z80asmfile)	!= '\n');	/* prepare for next line */

	if (ident[0] ==	'#') {
		if ((stdpath = getenv("Z80_OZFILES")) != NULL) {
			strncpy(stringconst, stdpath, 255);	/* copy	standard path */
			if (255	- strlen(stringconst) -	strlen(ident) >	0) {
				strcat(stringconst, ident + 1);	/* concatenate path and	filename */
				strcpy(ident, stringconst);	/* new filename	generated */
			}
		} else {
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 30);
			strcpy(	ident, (ident+1) ); /* remove '#' from file name */
		}
	}
}


void			DeclModuleName(void)
{
	if (CURRENTMODULE->mname == NULL) {
		if (sym	== name) {
			if ((CURRENTMODULE->mname = AllocIdentifier(strlen(ident) + 1))	!= NULL)
				strcpy(CURRENTMODULE->mname, ident);
			else
				ReportError(NULL, 0, 3);
		} else
			ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 11);
	} else
		ReportError(CURRENTFILE->fname,	CURRENTFILE->line, 15);
}
