
#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include "symbol.h"
#include "config.h"

/* external functions */
char		       *AllocIdentifier(size_t len);
void			RemovePfixlist(struct expr * pfixexpr);
void			Z80pass1(void);
void			Z80pass2(void);
void			CreateLib(void);
void			LinkModules(void);
void			DeclSymGlobal(void);
void			FreeSym(symbol *node);
void			CreateDeffile(void);
void			WriteGlobal(symbol *node);
void			WriteMapFile(void);
void			CreateBinFile(void);
struct sourcefile      *Newfile(struct sourcefile * curfile, char *fname);
enum symbols		GetSym(void);
long			GetConstant(char *evalerr);
long			ReadLong(FILE *	fileid);
int			cmpidstr(symbol	*kptr, symbol *p);
int			DefineSymbol(char *identifier, long value, unsigned char symboltype);
int			DefineDefSym(char *ident, long value, unsigned char symtype, avltree **root);
symbol		       *CreateSymbol( char *identifier,	long value, unsigned char symboltype, struct module * symowner);


/* local functions */
void			prompt(void);
void			ReportError(char *filename, short linenr, int errnum);
void			SetAsmFlag(char	*flagid);
void			WriteHeader(void);
void			ReleaseFile(struct sourcefile *	srcfile);
void			ReleaseLibraries(void);
void			ReleaseOwnedFile(struct	usedfile * ownedfile);
void			ReleaseModules(void);
void			ReleaseFilenames(void);
void			ReleaseExprns(struct expression	* express);
void			CloseFiles(void);
void			CreateLibfile(char *libfilename);
char		       *Getfilename(void);
int			AssembleSourceFile(void);
int			TestAsmFile(void);
int			GetModuleSize(void);
symbol		       *createsym(symbol *symptr);
struct modules	       *AllocModuleHdr(void);
struct module	       *AllocModule(void);
struct liblist	       *AllocLibHdr(void);
struct libfile	       *AllocLib(void);
struct module	       *NewModule(void);
struct libfile	       *NewLibrary(void);
struct expression      *AllocExprHdr(void);
struct JRPC_Hdr	       *AllocJRaddrHdr(void);


#ifdef QDOS
#include <qdos.h>

char			_prog_name[] = "Z80asm";
char			_version[] = "1.01";
char			_copyright[] = "\x7f InterLogic 1993-96";

void			consetup_title();
void			(*_consetup) ()	= consetup_title;
int			(*_readkbd) (chanid_t, timeout_t, char *) = readkbd_move;
struct WINDOWDEF	_condetails =
{2, 1, 0, 7, 484, 256, 0, 0};

#endif

char			copyrightmsg[] = "Z80 Module Assembler V1.01, (c) InterLogic 1993-96";

FILE		       *z80asmfile, *listfile, *errfile, *objfile, *mapfile, *modsrcfile, *deffile, *libfile;

char		       *errmsg[] =
{"File open/read error",        /* 0  */
 "Syntax error",		/* 1  */
 "Symbol not defined",		/* 2  */
 "Not enough memory",		/* 3  */
 "Integer out of range",	/* 4  */
 "Syntax error in expression",	/* 5  */
 "Right bracket missing",	/* 6  */
 "Out of range",		/* 7  */
 "Source filename missing",	/* 8  */
 "Illegal option",		/* 9  */
 "Unknown identifier",		/* 10 */
 "Illegal identifier",		/* 11 */
 "Max. code size reached",	/* 12 */
 "errors occurred during assembly",	/* 13 */
 "Symbol already defined",	/* 14 */
 "Module name already defined",	/* 15 */
 "Module name not defined",	/* 16 */
 "Symbol already declared local",	/* 17 */
 "Symbol already declared global",	/* 18 */
 "Symbol already declared external",	/* 19 */
 "No command line arguments",	/* 20 */
 "Illegal source filename",	/* 21 */
 "Symbol declared global in another module",	/* 22 */
 "Re-declaration not allowed",	/* 23 */
 "ORG already defined",		/* 24 */
 "Relative jump address must be local",	/* 25 */
 "Not an object file",	    /* 26 */
 "Reserved name",		/* 27 */
 "Couldn't open library file",	/* 28 */
 "Not a library file",		/* 29 */
 "Environment variable not defined" /* 30 */
};

enum symbols		sym, ssym[] =
			{space, strconq, dquote, squote, semicolon, comma, fullstop,
			 lparen, lcurly, rcurly, rparen, plus, minus, multiply,	divi, mod, power,
			 assign, bin_and, bin_or, bin_xor, less, greater, log_not, constexpr};

enum flag		pass1, listing,	listing_CPY, symtable, z80bin, writeline, mapref, globaldef, datestamp;
enum flag		deforigin, verbose, ASMERROR, EOL, symfile, library, createlibrary, autorelocate;
enum flag		codesegment;

int			ASSEMBLE_ERROR,	ERRORS,	TOTALERRORS, PAGENR, LINENR;
long			TOTALLINES;
int			sourcefile_open;
char			PAGELEN;
int			TAB_DIST = 8, COLUMN_WIDTH;
char			separators[] = " &\"\';,.({})+-*/%^=~|:<>!#";
char			line[255], stringconst[255], ident[255];
char		       *srcfilename, *lstfilename, *objfilename, *errfilename, *libfilename;

#ifdef QDOS
char			srcext[] = "_asm", lstext[] = "_lst", objext[] = "_obj", defext[] = "_def", errext[] = "_err";
char			binext[] = "_bin", libext[] = "_lib", symext[] = "_sym", mapext[] = "_map";
char			segmbinext[] = "_bn0";
#else
char			srcext[] = ".asm", lstext[] = ".lst", objext[] = ".obj", defext[] = ".def", binext[] = ".bin";
char			mapext[] = ".map", errext[] = ".err", symext[] = ".sym", libext[] = ".lib";
char			segmbinext[] = ".bn0";
#endif

char			Z80objhdr[] = "Z80RMF01";
char			objhdrprefix[] = "oomodnexprnamelibnmodc";
char			Z80libhdr[] = "Z80LMF01";

unsigned char	reloc_routine[]	=
			"\x08\xD9\xFD\xE5\xE1\x01\x49\x00\x09\x5E\x23\x56\xD5\x23\x4E\x23"
			"\x46\x23\xE5\x09\x44\x4D\xE3\x7E\x23\xB7\x20\x06\x5E\x23\x56\x23"
			"\x18\x03\x16\x00\x5F\xE3\x19\x5E\x23\x56\xEB\x09\xEB\x72\x2B\x73"
			"\xD1\xE3\x2B\x7C\xB5\xE3\xD5\x20\xDD\xF1\xF1\xFD\x36\x00\xC3\xFD"
			"\x71\x01\xFD\x70\x02\xD9\x08\xFD\xE9";

size_t			sizeof_relocroutine = 73;

char		       *reloctable = NULL, *relocptr = NULL;

long			listfileptr;
unsigned char	       *codearea, *codeptr;
size_t			CODESIZE;
unsigned short		PC, oldPC;		/* Program Counter */
size_t			EXPLICIT_ORIGIN;	/* origin defined from command line */
time_t			asmtime;		/* time	of assembly in seconds */
char		       *date;			/* pointer to datestring calculated from asmtime */

struct modules	       *modulehdr;
struct module	       *CURRENTMODULE;
struct liblist	       *libraryhdr;
avltree		       *globalroot, *staticroot;




int			AssembleSourceFile(void)
{

	if ((errfile = fopen(errfilename, "w"))	== NULL) {      /* Create error file */
		ReportError(NULL, 0, 0);
		return 0;
	}
	if (listing_CPY	|| symfile) {
		if ((listfile =	fopen(lstfilename, "w+")) != NULL) {    /* Create LIST or SYMBOL file */
			PAGENR = 0;
			LINENR = 6;
			WriteHeader();	/* Begin list file with	a header */
			listfileptr = ftell(listfile);	/* Get file pos. of next line in list file */
		} else {
			ReportError(NULL, 0, 0);
			return 0;
		}
	}
	if ((objfile = fopen(objfilename, "w+b")) != NULL) {    /* Create relocatable object file */
		fwrite(Z80objhdr, sizeof(char),	strlen(Z80objhdr), objfile);
		fwrite(objhdrprefix, sizeof(char), strlen(objhdrprefix), objfile);
	} else {
		ReportError(NULL, 0, 0);
		return 0;
	}

	PC = oldPC = 0;
	copy(staticroot, &CURRENTMODULE->localroot, (int (*)())	cmpidstr, (void	*(*)())	createsym);
	if ( DefineDefSym(ASSEMBLERPC, PC, 0, &globalroot) == 0) {              /* Create standard 'ASMPC' identifier */
		ReportError(NULL, 0, 3);
		return 0;
	}

	if (verbose) printf("Assembling '%s'...\nPass1...\n", srcfilename);

	pass1 =	ON;
	Z80pass1();
	pass1 =	OFF;		/* GetSymPtr will only generate	page references	in Pass1 (if listing is	ON) */

	/*
	 * Source file no longer needed	(file could already have been closed, if fatal error occurred during INCLUDE
	 * processing).
	 */
	if (sourcefile_open) {
		fclose(z80asmfile);
		z80asmfile = NULL;
	}
	if (CURRENTMODULE->mname == NULL)	/* Module name must be defined */
		ReportError(CURRENTFILE->fname,	0, 16);

	if (ERRORS == 0) {
		if (verbose)
			puts("Pass2...");
		Z80pass2();
	}
	if (listing_CPY	|| symfile) {
		fseek(listfile,	0, SEEK_END);
		fputc(12, listfile);	/* end listing with a FF */
		fclose(listfile);
		listfile = NULL;
		if (ERRORS)
			remove(lstfilename);	/* remove incomplete list file */
	}
	fclose(objfile);
	objfile	= NULL;
	if (ERRORS)
		remove(objfilename);	/* remove incomplete object file */

	if (errfile != NULL) {
		fclose(errfile);
		errfile	= NULL;
		if (ERRORS == 0	&& errfilename != NULL)
			remove(errfilename);	/* remove empty	error file */
	}
	if (globaldef) {
		fputc('\n', deffile);	/* separate DEFC lines for each	module */
		inorder(globalroot, (void (*)()) WriteGlobal);
	}
	deleteall(&CURRENTMODULE->localroot, (void (*)()) FreeSym);
	deleteall(&CURRENTMODULE->notdeclroot, (void (*)()) FreeSym);
	deleteall(&globalroot, (void (*)()) FreeSym);

	return 1;
}


void			ReleaseFilenames(void)
{
	if (srcfilename	!= NULL)
		free(srcfilename);
	if (lstfilename	!= NULL)
		free(lstfilename);
	if (objfilename	!= NULL)
		free(objfilename);
	if (errfilename	!= NULL)
		free(errfilename);
	srcfilename = lstfilename = objfilename	= errfilename =	NULL;
}


void			CloseFiles(void)
{
	if (z80asmfile != NULL)
		fclose(z80asmfile);
	if (listfile !=	NULL)
		fclose(listfile);
	if (objfile != NULL)
		fclose(objfile);
	if (errfile != NULL)
		fclose(errfile);
	z80asmfile = listfile =	objfile	= errfile = NULL;
}


int			TestAsmFile(void)
{
	struct stat		afile, ofile;

	if (datestamp) {        /* assemble only updated files */
		if (stat(srcfilename, &afile) == -1)
			return GetModuleSize();	/* source file not available...	*/
		else if	(stat(objfilename, &ofile) != -1)
			if (afile.st_mtime <= ofile.st_mtime)
				return GetModuleSize();	/* source is older than	object module */
	}
	if ((z80asmfile	= fopen(srcfilename, "r")) == NULL) {   /* Open source file */
		ReportError(srcfilename, 0, 0);	/* Object module is not	found or */
		return -1;			/* source is has recently been updated */
	}
	sourcefile_open	= 1;
	return 1;		/* assemble if no datestamp check */
}



int			GetModuleSize(void)
{
	char			fheader[9];
	long			fptr_modcode, fptr_modname;
	long			highbyte, lowbyte;
	size_t			size;

	if ((objfile = fopen(objfilename, "rb")) != NULL) {     /* open relocatable object file */
		fread(fheader, 1U, 8U, objfile);      /* read first 8 chars from file into array */
		fheader[8] = '\0';

		if (strcmp(fheader, Z80objhdr) != 0) {  /* compare header of file */
			ReportError(objfilename, 0, 26);	/* not an object file */
			fclose(objfile);
			objfile	= NULL;
			return -1;
		}
		fseek(objfile, 8 + 2, SEEK_SET);	/* set file pointer to point at	module name */
		fptr_modname = ReadLong(objfile);	/* get file pointer to module name */
		fseek(objfile, fptr_modname, SEEK_SET);	/* set file pointer to module name */

		size = fgetc(objfile);
		fread(line, sizeof(char), size,	objfile);	/* read	module name */
		line[size] = '\0';
		if ((CURRENTMODULE->mname = AllocIdentifier(size + 1)) == NULL)	{
			ReportError(NULL, 0, 3);
			return -1;
		} else
			strcpy(CURRENTMODULE->mname, line);

		fseek(objfile, 26, SEEK_SET);	/* set file pointer to point at	module code pointer */
		fptr_modcode = ReadLong(objfile);	/* get file pointer to module code */
		if (fptr_modcode != -1)	{
			fseek(objfile, fptr_modcode, SEEK_SET);	/* set file pointer to module code */
			lowbyte	= fgetc(objfile);
			highbyte = fgetc(objfile);
			size = lowbyte + highbyte * 256;
			if (CURRENTMODULE->startoffset + size >	MAXCODESIZE)
				ReportError(objfilename, 0, 12);
			else
				CODESIZE += size;
		}
		fclose(objfile);
		return 0;
	} else {
		ReportError(NULL, 0, 0);
		return -1;
	}
}


void			CreateLibfile(char *filename)
{
	size_t			l;

	l = strlen(filename);
	if (l) {
		if (strcmp(filename + (l - 4), libext) != 0) {  /* 'lib' extension not specified */
			if ((libfilename = AllocIdentifier(l + 4 + 1)) != NULL)	{
				strcpy(libfilename, filename);
				strcat(libfilename, libext);	/* add '_lib' extension	 */
			} else {
				ReportError(NULL, 0, 3);
				return;
			}
		} else {
			if ((libfilename = AllocIdentifier(l + 1)) != NULL)	/* 'lib' extension specified */
				strcpy(libfilename, filename);
			else {
				ReportError(NULL, 0, 3);
				return;
			}
		}
	} else {
		if ((filename =	getenv("Z80_STDLIB")) != NULL) {
			if ((libfilename = AllocIdentifier(strlen(filename))) != NULL)
				strcpy(libfilename, filename);
			else {
				ReportError(NULL, 0, 3);
				return;
			}
		} else {
			ReportError(NULL, 0, 30);
			return;
		}
	}

	if ((libfile = fopen(libfilename, "w+b")) == NULL) {    /* create library as BINARY file */
		free(libfilename);
		libfilename = NULL;
		ReportError(libfilename, 0,28);
	} else {
		createlibrary =	ON;
		fwrite(Z80libhdr, sizeof(char),	8U, libfile);  /* write	library	header */
	}
}


void			GetLibfile(char	*filename)
{
	struct libfile	       *newlib;
	char		       *f, fheader[9];
	int			l;

	if ((newlib = NewLibrary()) == NULL) {
		ReportError(NULL, 0, 3);
		return;
	}

	l = strlen(filename);
	if (l) {
		if (strcmp(filename + (l - 4), libext) != 0) {  /* 'lib' extension not specified */
			if ((f = AllocIdentifier(l + 4 + 1)) !=	NULL) {
				strcpy(f, filename);
				strcat(f, libext);	/* add '_lib' extension	 */
			} else {
				ReportError(NULL, 0, 3);
				return;
			}
		} else {
			if ((f = AllocIdentifier(l + 1)) != NULL)	/* 'lib' extension specified */
				strcpy(f, filename);
			else {
				ReportError(NULL, 0, 3);
				return;
			}
		}
	} else {
		filename = getenv("Z80_STDLIB");
		if (filename !=	NULL) {
			if ((f = AllocIdentifier(strlen(filename))) != NULL)
				strcpy(f, filename);
			else {
				ReportError(NULL, 0, 3);
				return;
			}
		} else {
			ReportError(NULL, 0, 30);
			return;
		}
	}

	newlib->libfilename = f;
	if ((z80asmfile	= fopen(f, "rb")) == NULL) {    /* Does file exist? */
		ReportError(NULL, 0, 28);
		return;
	} else {
		fread(fheader, 1U, 8U, z80asmfile);	/* read	first 8	chars from file	into array */
		fheader[8] = '\0';
	}

	if (strcmp(fheader, Z80libhdr) != 0)		/* compare header of file */
		ReportError(f, 0, 29);			/* not a library file */
	else
		library	= ON;

	fclose(z80asmfile);
	z80asmfile = NULL;
}



void			SetAsmFlag(char	*flagid)
{
	int			i;

	if (strcmp(flagid, "c")	== 0) {
		codesegment = ON;
		return;
	}
	if (strcmp(flagid, "a")	== 0) {
		z80bin = ON;
		datestamp = ON;
		return;
	}
	if (strcmp(flagid, "l")	== 0) {
		listing_CPY = listing =	ON;
		if (symtable)
			symfile	= OFF;
		return;
	}
	if (strcmp(flagid, "nl") == 0) {
		listing_CPY = listing =	OFF;
		if (symtable)
			symfile	= ON;
		return;
	}
	if (strcmp(flagid, "s")	== 0) {
		symtable = ON;
		if (listing_CPY)
			symfile	= OFF;
		else
			symfile	= ON;
		return;
	}
	if (strcmp(flagid, "ns") == 0) {
		symtable = symfile = OFF;
		return;
	}
	if (strcmp(flagid, "nb") == 0) {
		z80bin = OFF;
		mapref = OFF;
		return;
	}
	if (strcmp(flagid, "b")	== 0) {
		z80bin = ON;	/* perform address relocation &	linking	*/
		return;
	}
	if (strcmp(flagid, "v")	== 0) {
		verbose	= ON;	/* perform address relocation &	linking	*/
		return;
	}
	if (strcmp(flagid, "nv") == 0) {
		verbose	= OFF;	/* perform address relocation &	linking	*/
		return;
	}
	if (strcmp(flagid, "d")	== 0) {
		datestamp = ON;	/* assemble only if source > object file */
		return;
	}
	if (strcmp(flagid, "nd") == 0) {
		datestamp = OFF;
		return;
	}
	if (strcmp(flagid, "m")	== 0) {
		mapref = ON;
		return;
	}
	if (strcmp(flagid, "g")	== 0) {
		globaldef = ON;
		return;
	}
	if (strcmp(flagid, "ng") == 0) {
		globaldef = OFF;
		return;
	}
	if (strcmp(flagid, "nm") == 0) {
		mapref = OFF;
		return;
	}
	if (strcmp(flagid,"nR")	== 0) {
		autorelocate = OFF;
		return;
	}
	if (*flagid == 'i') {
		GetLibfile( (flagid+1) );
		return;
	}
	if (*flagid == 'x') {
		CreateLibfile( (flagid+1) );
		return;
	}
	if (*flagid == 'r') {
		sscanf(flagid +	1, "%x", &EXPLICIT_ORIGIN);
		deforigin = ON;	/* explicit origin has been defined */
		return;
	}
	if (*flagid == 'R') {
		autorelocate = ON;
		return;
	}
	if (*flagid == 't') {
		sscanf(flagid +	1, "%d", &TAB_DIST);
		return;
	}
	if (*flagid == 'D') {
		strcpy(ident, (flagid +	1));	/* copy	argument string	*/
		if (!isalpha(ident[0]))	{
			ReportError(NULL, 0, 11);	/* symbol must begin with alpha	*/
			return;
		}
		i = 0;
		while (ident[i]	!= '\0') {
			if (strchr(separators, ident[i]) == NULL) {
				if (!isalnum(ident[i]))	{
					if (ident[i] !=	'_') {
						ReportError(NULL, 0, 11);	/* illegal char	in identifier */
						return;
					} else
						ident[i] = '_';	/* underscore in identifier */
				} else
					ident[i] = toupper(ident[i]);
			} else {
				ReportError(NULL, 0, 11);	/* illegal char	in identifier */
				return;
			}
			++i;
		}
		DefineDefSym(ident, 1, 0, &staticroot);
	}
}


char		       *Getfilename(void)
{
	char		       *inp;

	if ((inp = fgets(line, 255, modsrcfile)) != NULL)
		line[strlen(line) - 1] = '\0';	/* remove linefeed */
	return inp;
}


struct module	       *NewModule(void)
{
	struct module	       *newm;

	if (modulehdr == NULL) {
		if ((modulehdr = AllocModuleHdr()) == NULL)
			return NULL;
		else {
			modulehdr->first = NULL;
			modulehdr->last	= NULL;	/* Module header initialised */
		}
	}
	if ((newm = AllocModule()) == NULL)
		return NULL;
	else {
		newm->nextmodule = NULL;
		newm->mname = NULL;
		newm->startoffset = CODESIZE;
		newm->origin = 65535;
		newm->cfile = NULL;
		newm->localroot	= NULL;
		newm->notdeclroot = NULL;

		if ((newm->mexpr = AllocExprHdr()) != NULL) {   /* Allocate room for expression header */
			newm->mexpr->firstexpr = NULL;
			newm->mexpr->currexpr =	NULL;	/* Module expression header initialised	*/
		} else {
			free(newm);	/* remove partial module definition */
			return NULL;	/* No room for header */
		}

		if ((newm->JRaddr = AllocJRaddrHdr()) != NULL) {
			newm->JRaddr->firstref = NULL;
			newm->JRaddr->lastref =	NULL;	/* Module JRaddr list header initialised */
		} else {
			free(newm->mexpr);	/* remove expression header */
			free(newm);	/* remove partial module definition */
			return NULL;	/* No room for header */
		}
	}

	if (modulehdr->first ==	NULL) {
		modulehdr->first = newm;
		modulehdr->last	= newm;	/* First module	in list	*/
	} else {
		modulehdr->last->nextmodule = newm;	/* current/last	module points now at new current */
		modulehdr->last	= newm;	/* pointer to current module updated */
	}

	return newm;
}


struct libfile	       *NewLibrary(void)
{
	struct libfile	       *newl;

	if (libraryhdr == NULL)	{
		if ((libraryhdr	= AllocLibHdr()) == NULL)
			return NULL;
		else {
			libraryhdr->firstlib = NULL;
			libraryhdr->currlib = NULL;	/* Library header initialised */
		}
	}
	if ((newl = AllocLib())	== NULL)
		return NULL;
	else {
		newl->nextlib =	NULL;
		newl->libfilename = NULL;
		newl->nextobjfile = -1;
	}

	if (libraryhdr->firstlib == NULL) {
		libraryhdr->firstlib = newl;
		libraryhdr->currlib = newl;	/* First library in list */
	} else {
		libraryhdr->currlib->nextlib = newl;	/* current/last	library	points now at new current */
		libraryhdr->currlib = newl;	/* pointer to current module updated */
	}

	return newl;
}



void			ReleaseModules(void)
{
	struct module	       *tmpptr,	*curptr;

	if (modulehdr == NULL) return;

	curptr = modulehdr->first;
	do {
		if (curptr->cfile != NULL)
			ReleaseFile(curptr->cfile);
		deleteall(&curptr->localroot, (void (*)()) FreeSym);
		deleteall(&curptr->notdeclroot,	(void (*)()) FreeSym);
		if (curptr->mexpr != NULL) ReleaseExprns(curptr->mexpr);
		if (curptr->mname != NULL) free(curptr->mname);
		tmpptr = curptr;
		curptr = curptr->nextmodule;
		free(tmpptr);	/* Release module */
	}
	while (curptr != NULL);	/* until all modules are released */

	free(modulehdr);
	modulehdr = NULL;
	CURRENTMODULE =	NULL;
}



void			ReleaseLibraries(void)
{
	struct libfile	       *curptr,	*tmpptr;

	curptr = libraryhdr->firstlib;
	do {
		if (curptr->libfilename	!= NULL)
			free(curptr->libfilename);
		tmpptr = curptr;
		curptr = curptr->nextlib;
		free(tmpptr);	/* release library */
	}
	while (curptr != NULL);	/* until all libraries are released */

	free(libraryhdr);	/* Release library header */
	libraryhdr = NULL;
}



void			ReleaseExprns(struct expression	* express)
{
	struct expr	       *tmpexpr, *curexpr;

	curexpr	= express->firstexpr;
	while (curexpr != NULL)	{
		tmpexpr	= curexpr->nextexpr;
		RemovePfixlist(curexpr);
		curexpr	= tmpexpr;
	}

	free(express);
}


void			ReleaseFile(struct sourcefile *	srcfile)
{
	if (srcfile->usedsourcefile != NULL)
		ReleaseOwnedFile(srcfile->usedsourcefile);
	free(srcfile->fname);	/* Release allocated area for filename */
	free(srcfile);		/* Release file	information record for this file */
}


void			ReleaseOwnedFile(struct	usedfile * ownedfile)
{
	/* Release first other files called by this file */
	if (ownedfile->nextusedfile != NULL)
		ReleaseOwnedFile(ownedfile->nextusedfile);

	/* Release first file owned by this file */
	if (ownedfile->ownedsourcefile != NULL)
		ReleaseFile(ownedfile->ownedsourcefile);

	free(ownedfile);	/* Then	release	this owned file	*/
}


void			ReportError(char *filename, short linenr, int errnum)
{
	ASSEMBLE_ERROR = errnum;/* set the global error	variable for general error trapping */
	ASMERROR = ON;

	if (errfile == NULL) {
		if (filename !=	NULL)
			printf("In file '%s', ", filename);
		if (linenr != 0)
			printf("at line %d, ", linenr);
	} else {
		if (filename !=	NULL)
			fprintf(errfile, "In file '%s', ", filename);
		if (linenr != 0)
			fprintf(errfile, "at line %d, ", linenr);
	}

	if (errnum == 13)
		printf("%d %s\n", TOTALERRORS, errmsg[errnum]);
	else {
		if (errfile == NULL)
			printf("%s\n", errmsg[errnum]);
		else
			fprintf(errfile, "%s\n", errmsg[errnum]);
	}

	++ERRORS;
	++TOTALERRORS;
}


void	display_options(void)
{
	if (datestamp == ON) puts("Assemble only updated files."); else	puts("Assemble all files");
	if (symfile == ON) puts("Create symbol table file.");
	if (listing == ON) puts("Create listing file.");
	if (globaldef == ON) puts("Create global definition file.");
	if (createlibrary == ON) puts("Create library from specified modules.");
	if (z80bin == ON) puts("Link/relocate assembled modules.");
	if (library == ON) puts("Link library modules with code.");
	if (z80bin == ON && mapref == ON) puts("Create address map file.");
	if (codesegment	== ON && autorelocate == OFF) puts("Split code into 16K banks.");
	if (autorelocate == ON)	puts("Create relocatable code.");
	putchar('\n');
}


/***************************************************************************************************
 * Main	entry of Z80asm
 ***************************************************************************************************/
int			main(int argc, char *argv[])
{
	char		       *tmpname;
	int			asmflag;

	printf("%s\n", copyrightmsg);

	symtable = symfile = writeline = verbose = mapref = ON;
	listing	= listing_CPY =	z80bin = datestamp = ASMERROR =	codesegment = OFF;
	deforigin = globaldef =	library	= createlibrary	= autorelocate = OFF;

	libfilename = NULL;
	modsrcfile = NULL;
	CURRENTMODULE =	NULL;
	modulehdr = NULL;	/* initialise to no modules */
	libraryhdr = NULL;	/* initialise to no library files */

	globalroot = NULL;	/* global identifier tree initialized */
	staticroot = NULL;	/* static identifier tree initialized */

	asmflag	= DefineDefSym(OS_ID, 1, 0, &staticroot);
	if (!asmflag) exit(-1);

	/* Get command line arguments, if any... */
	if (argc == 1) {
		prompt();
		exit(-1);
	}
	time(&asmtime);
	date = asctime(localtime(&asmtime));	/* get current system time for date in list file */

	codearea = (unsigned char *) calloc(MAXCODESIZE, sizeof(char));	/* Allocate Memory for Z80 machine code	*/
	if (codearea ==	NULL) {
		ReportError(NULL, 0, 3);
		exit(-1);
	}
	CODESIZE = 0;

	PAGELEN	= 66;
	TOTALERRORS = 0;
	TOTALLINES =0;

	if ((CURRENTMODULE = NewModule()) == NULL) {    /* then create a dummy module */
		ReportError(NULL, 0, 3);		/* this	is needed during command line parsing */
		exit(-1);
	}
	while (--argc >	0) {    /* Get options first */
		++argv;
		if ((*argv)[0] == '-')
			SetAsmFlag(((*argv) + 1));
		else {
			if ( (*argv)[0]	== '#')
				if ((modsrcfile	= fopen( (*argv	+ 1), "r")) == NULL)
					ReportError(NULL, 0, 0);
			break;
		}
	}

	ReleaseModules();	/* Now remove dummy module again, not needed */

	if (!argc && modsrcfile	== NULL) {
		ReportError(NULL, 0, 8);
		exit(-1);
	}
	COLUMN_WIDTH = 4 * TAB_DIST;	/* define column width	for output files */

	if (verbose == ON) display_options();	   /* display status messages of select	assembler options */

	for (;;) {              /* Module loop */
		z80asmfile = listfile =	objfile	= errfile = NULL;

		codeptr	= codearea;	/* Pointer (PC)	to store z80 instruction */
		ERRORS = 0;
		ASSEMBLE_ERROR = -1;	/* General error flag */

		if (modsrcfile == NULL)	{
			if (argc > 0) {
				if ((*argv)[0] != '-') {
					tmpname	= *argv;
					--argc;
					++argv;	/* get ready for next filename */
				} else {
					ReportError(NULL, 0, 21);	/* Illegal source file name */
					break;
				}
			} else
				break;
		} else {
			if ((tmpname = Getfilename()) == NULL) {
				fclose(modsrcfile);
				break;
			}
		}

		if ((srcfilename = AllocIdentifier(strlen(tmpname) + 5)) != NULL) {
			strcpy(srcfilename, tmpname);
			strcat(srcfilename, srcext);	/* add '_src' extension	 */
		} else {
			ReportError(NULL, 0, 3);
			break;
		}
		if ((objfilename = AllocIdentifier(strlen(srcfilename) + 1)) !=	NULL) {
			strcpy(objfilename, srcfilename);
			strcpy(objfilename + strlen(srcfilename) - 4, objext);	/* overwrite '_asm' extension with
										 * '_obj' */
		} else {
			ReportError(NULL, 0, 3);
			break;	/* No more room	*/
		}

		if ((lstfilename = AllocIdentifier(strlen(srcfilename) + 1)) !=	NULL) {
			strcpy(lstfilename, srcfilename);
			if (listing)
				strcpy(lstfilename + strlen(srcfilename) - 4, lstext);	/* overwrite '_asm' extension
											 * with	'_lst' */
			else
				strcpy(lstfilename + strlen(srcfilename) - 4, symext);	/* overwrite '_asm' extension
											 * with	'_sym' */
		} else {
			ReportError(NULL, 0, 3);
			break;	/* No more room	*/
		}

		if ((errfilename = AllocIdentifier(strlen(srcfilename) + 1)) !=	NULL) {
			strcpy(errfilename, srcfilename);
			strcpy(errfilename + strlen(srcfilename) - 4, errext);	/* overwrite '_asm' extension with
										 * '_err' */
		} else {
			ReportError(NULL, 0, 3);
			break;	/* No more room	*/
		}

		if ((CURRENTMODULE = NewModule()) == NULL) {    /* Create module data structures for new file */
			ReportError(NULL, 0, 3);
			break;
		}
		if ((CURRENTFILE = Newfile(NULL, srcfilename)) == NULL)
			break;	/* Create first	file record, if	possible */

		if (globaldef && CURRENTMODULE == modulehdr->first)
			CreateDeffile();

		if ((asmflag = TestAsmFile()) == 1) {
			AssembleSourceFile();	/* begin assembly... */
			if (verbose)
				putchar('\n');	/* separate module texts */
		} else if (asmflag == -1)
			break;	/* file	open error - stop assembler */

		ReleaseFilenames();
	}			/* for */

	ReleaseFilenames();
	CloseFiles();

	if (globaldef)
		fclose(deffile);

	if (createlibrary && ASMERROR == OFF)
		CreateLib();

	if (createlibrary) {
		fclose(libfile);
		if (ASMERROR)
			remove(libfilename);
		free(libfilename);
		libfilename = NULL;
	}

	if (ASMERROR==OFF && verbose) printf("Total of %d lines assembled.\n", TOTALLINES);

	if (ASMERROR==OFF && z80bin) LinkModules();

	if (TOTALERRORS==0 & z80bin) {
		if (mapref) WriteMapFile();
		CreateBinFile();
	}
	ReleaseFilenames();
	CloseFiles();

#ifndef	QDOS
	deleteall(&globalroot, (void (*)()) FreeSym);
	deleteall(&staticroot, (void (*)()) FreeSym);

	if (modulehdr != NULL) ReleaseModules();       /* Release module information (symbols, etc.) */

	if (libraryhdr != NULL)	ReleaseLibraries();	/* Release library information */
	free(codearea);		/* Release area	for machine code */

	if (autorelocate) if (reloctable != NULL) free(reloctable);
#endif

	if (ASMERROR) ReportError(NULL,	0, 13);

	return (0);		/* assembler successfully ended	*/
}


void			prompt(void)
{
	puts("z80asm [options] [ #<modulefile> | {<filename>} ]");
	puts("[] = may be ignored. {} = may be repeated. | = OR clause.");
	printf("File names are specified without extensions. To assemble 'fred%s' use 'fred'.\n", srcext);
	puts("<modulefile> contains file names of all modules to be linked (with no ext.):");
	puts("File names are put on separate lines ended with \\n. File types recognised or");
	puts("created by z80asm (defined by the following extension):");
	printf("%s = source file, %s = object file, %s = list file, %s = Z80 code file\n", srcext, objext, lstext, binext);
	printf("%s = symbols file, %s = map file, %s = XDEF file, %s = error file\n", symext, mapext, defext, errext);
	puts("");
	puts("Options: -n defines option to be turned OFF (except -r -R -i -x -D -t).");
	puts("-v verbose   -l listing file   -s symbol table (in listing or symbol file)");
	puts("-r<ORG> Explicit relocation <ORG> defined in hex (ignore ORG in first module).");
	puts("-R Generate relocatable code (Automatical relocation before execution).");
	puts("-D<symbol> define symbol as logically TRUE (used for conditional assembly).");
	puts("-b assemble files, link & relocate to ORG address. -c split code in 16K banks.");
	puts("-d date stamp control, assemble only if source file > object file.");
	puts("-a: -b & -d (assemble only updated source files, then link & relocate).");
	puts("-m map listing file   -g XDEF reloc. address definitions from all modules.");
	printf("-i<library> include <library> LIB modules with %s modules during linking.\n", objext);
	puts("-x<library> create library from specified modules ( e.g. with #<modules> ).");
	printf("-t<n> tabulator width for %s, %s, %s files. Column width is 4 times -t.\n", mapext, defext, symext);
	puts("Default options: -v -nd -nb -nl -s -m -ng -nc -nR -t8");
}


symbol		       *createsym(symbol *symptr)
{
	return CreateSymbol( symptr->symname, symptr->symvalue,	symptr->type, symptr->owner);
}


struct expression      *AllocExprHdr(void)
{
	return (struct expression *) malloc(sizeof(struct expression));
}

struct JRPC_Hdr	       *AllocJRaddrHdr(void)
{
	return (struct JRPC_Hdr	*) malloc(sizeof(struct	JRPC_Hdr));
}

struct modules	       *AllocModuleHdr(void)
{
	return (struct modules *) malloc(sizeof(struct modules));
}

struct module	       *AllocModule(void)
{
	return (struct module *) malloc(sizeof(struct module));
}

struct liblist	       *AllocLibHdr(void)
{
	return (struct liblist *) malloc(sizeof(struct liblist));
}

struct libfile	       *AllocLib(void)
{
	return (struct libfile *) malloc(sizeof(struct libfile));
}
