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

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


/* external functions */
void			FreeSym(symbol *node);
void			ReportError(char *filename, short linenr, int errnum);
void			RemovePfixlist(struct expr * pfixexpr);
char		       *AllocIdentifier(size_t len);
struct module	       *NewModule(void);
struct libfile	       *NewLibrary(void);
struct sourcefile      *Newfile(struct sourcefile * curfile, char *fname);
long			EvalPfixExpr(struct expr * pass2expr);
int			DefineDefSym(char *identifier, long value, unsigned char symtype, avltree **root);
int			cmpidstr(symbol	*kptr, symbol *p);
int			cmpidval(symbol	*kptr, symbol *p);
struct expr	       *ParseNumExpr(void);
symbol		       *FindSymbol(char	*identifier, avltree *symbolptr);
symbol		       *CreateSymbol(char *identifier, long value, unsigned char symboltype, struct module * symowner);

/* local functions */
int			LinkModule(char	*filename, long	fptr_base);
int			LinkTracedModule(char *filename, long baseptr);
int			LinkLibModules(char *objfilename, long fptr_base, long startnames, long	endnames);
int			LinkLibModule(struct libfile * library,	long curmodule,	char *modname);
int			SearchLibfile(struct libfile * curlib, char *modname);
char		       *ReadName(void);
long			ReadLong(FILE *	fileid);
void			redefinedmsg();
void			CreateLib(void);
void			SearchLibraries(char *modname);
void			WriteLong(long fptr, FILE * fileid);
void			LinkModules(void);
void			ModuleExpr(void);
void			CreateBinFile(void);
void			WriteExprMsg(void);
void			DefineOrigin(void);
void			WriteMapFile(void);
void			ReadNames(long nextname, long endnames);
void			ReadExpr(long nextexpr,	long endexpr);
void			ReOrderSymbol(avltree *node, avltree **maproot,	int (*symcmp) (void *, void *));
void			WriteMapSymbol(symbol *mapnode);
void			WriteGlobal(symbol *node);
void			CreateDeffile(void);
void			ReleaseLinkInfo(void);
struct linklist	       *AllocLinkHdr(void);
struct linkedmod       *AllocTracedModule(void);

/* global variables */
extern FILE	       *listfile, *mapfile, *z80asmfile, *errfile, *deffile, *libfile;
extern char		line[],	ident[];
extern char	       *lstfilename, *objfilename, *errfilename, *libfilename;
extern char		objext[], segmbinext[],	binext[], mapext[], errext[], defext[];
extern char		Z80objhdr[];
extern enum symbols	sym, GetSym(void);
extern enum flag	listing, writeline, symtable, mapref, z80bin, autorelocate, codesegment;
extern enum flag	verbose, deforigin, globaldef, EOL, library, ASMERROR;
extern unsigned	short	PC;
extern long		EXPLICIT_ORIGIN;
extern size_t		CODESIZE;
extern unsigned	char   *codearea, PAGELEN;
extern unsigned	char	reloc_routine[];
extern int		ASSEMBLE_ERROR,	listfileptr;
extern struct modules  *modulehdr;
extern struct liblist  *libraryhdr;
extern struct module   *CURRENTMODULE;
extern int		PAGENR,	TOTALERRORS;
extern int		TAB_DIST, COLUMN_WIDTH;
extern avltree	       *globalroot;
extern char	       *reloctable, *relocptr;
extern size_t		sizeof_relocroutine;


struct linklist	       *linkhdr;
struct libfile	       *CURRENTLIB;
unsigned short	       totaladdr, curroffset, sizeof_reloctable;


void			ReadNames(long nextname, long endnames)
{
	char			scope, symtype;
	long			value;
	symbol			*foundsymbol;

	do {
		scope =	fgetc(z80asmfile);
		symtype	= fgetc(z80asmfile);	/* type	of name	*/
		value =	ReadLong(z80asmfile);	/* read	symbol (long) integer */
		ReadName();	/* read	symbol name */
		nextname += 1 +	1 + 4 +	1 + strlen(line);

		switch (symtype) {
			case 'A':
				symtype	= SYMADDR | SYMDEFINED;
				value += modulehdr->first->origin + CURRENTMODULE->startoffset;	/* Absolute address */
				break;

			case 'C':
				symtype	= SYMDEFINED;
				break;
		}

		switch (scope) {
			case 'L':
				if ((foundsymbol = FindSymbol(line, CURRENTMODULE->localroot)) == NULL)	{
					foundsymbol = CreateSymbol(line, value,	symtype	| SYMLOCAL, CURRENTMODULE);
					if (foundsymbol	!= NULL)
						insert(&CURRENTMODULE->localroot, foundsymbol, (int (*)()) cmpidstr);
				} else {
					foundsymbol->symvalue =	value;
					foundsymbol->type |= symtype | SYMLOCAL;
					foundsymbol->owner = CURRENTMODULE;
					redefinedmsg();
				}
				break;

			case 'G':
				if ((foundsymbol = FindSymbol(line, globalroot)) == NULL) {
					foundsymbol = CreateSymbol(line, value,	symtype	| SYMXDEF, CURRENTMODULE);
					if (foundsymbol	!= NULL)
						insert(&globalroot, foundsymbol, (int (*)()) cmpidstr);
				} else {
					foundsymbol->symvalue =	value;
					foundsymbol->type |= symtype | SYMXDEF;
					foundsymbol->owner = CURRENTMODULE;
					redefinedmsg();
				}
				break;

			case 'X':
				if ((foundsymbol = FindSymbol(line, globalroot)) == NULL) {
					foundsymbol = CreateSymbol(line, value,	symtype	| SYMXDEF | SYMDEF, CURRENTMODULE);
					if (foundsymbol	!= NULL)
						insert(&globalroot, foundsymbol, (int (*)()) cmpidstr);
				} else {
					foundsymbol->symvalue =	value;
					foundsymbol->type |= symtype | SYMXDEF | SYMDEF;
					foundsymbol->owner = CURRENTMODULE;
					redefinedmsg();
				}
				break;
		}
	}
	while (nextname	< endnames);
}


void	redefinedmsg()
{
	printf("Symbol <%s> redefined in module '%s'\n", line, CURRENTMODULE->mname);
}


void			ReadExpr(long nextexpr,	long endexpr)
{
	char			type;
	long			lowbyte, highbyte, offsetptr;
	long			constant, i, fptr;
	struct expr	       *postfixexpr;
	unsigned char	       *patchptr;

	do {
		type = fgetc(z80asmfile);

		lowbyte	= fgetc(z80asmfile);
		highbyte = fgetc(z80asmfile);
		offsetptr = highbyte * 256U + lowbyte;

		/* assembler PC	as absolute address */
		PC = modulehdr->first->origin +	CURRENTMODULE->startoffset + offsetptr;
		FindSymbol(ASSEMBLERPC,	globalroot)->symvalue =	PC;
								
		i = fgetc(z80asmfile);	/* get length of infix expression */
		fptr = ftell(z80asmfile);	/* file	pointer	is at start of expression */
		fgets(line, i +	1, z80asmfile);	/* read	string for error reference */
		fseek(z80asmfile, fptr,	SEEK_SET);	/* reset file pointer to start of expression */
		nextexpr += 1 +	1 + 1 +	1 + i +	1;
		/* printf("%c, %d, %s\n", type,	offsetptr, line); */

		EOL = OFF;	/* reset end of	line parsing flag - a line is to be parsed... */
		GetSym();
		if ((postfixexpr = ParseNumExpr()) != NULL) {   /* parse numerical expression */
			if (postfixexpr->rangetype & NOTEVALUABLE) {
				ReportError(CURRENTFILE->fname,	0, 2);
				WriteExprMsg();
			} else {
				constant = EvalPfixExpr(postfixexpr);
				patchptr = codearea + CURRENTMODULE->startoffset + offsetptr;	/* absolute patch pos.
												 * in memory buffer */
				switch (type) {
					case 'U':
						if (constant >=	-128 &&	constant <= 255)
							*patchptr = (unsigned char) constant;
						else {
							ReportError(CURRENTFILE->fname,	0, 7);
							WriteExprMsg();
						}
						break;

					case 'S':
						if (constant >=	-128 &&	constant <= 255)
							*patchptr = (char) constant;	/* opcode is stored, now store
											 * relative jump */
						else {
							ReportError(CURRENTFILE->fname,	0, 7);
							WriteExprMsg();
						}
						break;

					case 'C':
						if (constant >=	-32768 && constant <= 65535) {
							*patchptr++ = (unsigned	short) constant	% 256U;
							*patchptr = (unsigned short) constant /	256U;
						} else {
							ReportError(CURRENTFILE->fname,	0, 7);
							WriteExprMsg();
						}

						if (autorelocate)
							if (postfixexpr->rangetype & SYMADDR) {
								/* Expression contains relocatable address */
								if ( (PC-curroffset>=0)	&& (PC-curroffset<=255)	) {
									*relocptr++ = PC-curroffset;
									sizeof_reloctable++;
								} else {
									*relocptr++ = 0;
									*relocptr++ = (unsigned	short) (PC-curroffset) % 256U;
									*relocptr++ = (unsigned	short) (PC-curroffset) / 256U;
									sizeof_reloctable += 3;
								}
								totaladdr++;
								curroffset = PC;
							}
						break;

					case 'L':
						if (constant >=	LONG_MIN && constant <=	LONG_MAX)
							for (i = 0; i <	4; i++)	{
								*patchptr++ = (unsigned	char) constant % 256U;
								constant >>= 8U;
							}
						else {
							ReportError(CURRENTFILE->fname,	0, 7);
							WriteExprMsg();
						}
						break;
				}
			}
			RemovePfixlist(postfixexpr);
		} else
			WriteExprMsg();
	}
	while (nextexpr	< endexpr);
}



void			WriteExprMsg(void)
{
	fprintf(errfile, "Error in expression %s\n\n", line);
}



void			LinkModules(void)
{
	char			fheader[9];
	size_t			lowbyte, highbyte;
	struct module	       *lastobjmodule;

	symtable = listing = OFF;
	linkhdr	= NULL;

	if (verbose)
		puts("linking module(s)...\nPass1...");

	if (autorelocate == ON)	{
		reloctable = (char *) malloc(32768U);
		if (reloctable == NULL)	{
			ReportError(NULL, 0, 3);
			return;		/* No more room	*/
		} else {
			relocptr = reloctable;
			relocptr += 4;		/* point at first offset to store */
			totaladdr = 0;
			sizeof_reloctable = 0;	/* relocation table, still 0 elements .. */
			curroffset = 0;
		}
	}

	CURRENTMODULE =	modulehdr->first;	/* begin with first module */
	lastobjmodule =	modulehdr->last;	/* remember this last module, further modules are libraries */

	if ((errfilename = AllocIdentifier(strlen(CURRENTFILE->fname) +	1)) != NULL) {
		strcpy(errfilename, CURRENTFILE->fname);
		strcpy(errfilename + strlen(errfilename) - 4, errext);	/* overwrite '_asm' extension with '_err' */
	} else {
		ReportError(NULL, 0, 3);
		return;		/* No more room	*/
	}
	if ((errfile = fopen(errfilename, "w"))	== NULL) {      /* open error file */
		ReportError(NULL, 0, 0);	/* couldn't open relocatable file */
		free(errfilename);
		errfilename = NULL;
		return;
	}

	PC = 0;
	if (DefineDefSym(ASSEMBLERPC, PC, 0, &globalroot) == 0)	{       /* Create standard 'ASMPC' identifier */
		ReportError(NULL, 0, 3);	/* no more room	*/
		free(errfilename);
		return;
	}

	do {                    /* link machine code & read symbols in all modules */
		if (library) {
			CURRENTLIB = libraryhdr->firstlib;	/* begin library search	from first library for each
								 * module */
			CURRENTLIB->nextobjfile	= 8;	/* point at first library module (past header) */
		}
		CURRENTFILE->line = 0;	/* no line references on errors	during link processing */

		if ((objfilename = AllocIdentifier(strlen(CURRENTFILE->fname) +	1)) != NULL) {
			strcpy(objfilename, CURRENTFILE->fname);
			strcpy(objfilename + strlen(objfilename) - 4, objext);	/* overwrite '_asm' extension with
										 * '_obj' */
		} else {
			ReportError(NULL, 0, 3);
			break; /* No more room */
		}

		if ((z80asmfile	= fopen(objfilename, "rb")) != NULL) {  /* open relocatable file for reading */
			fread(fheader, 1U, 8U, z80asmfile);   /* read first 6 chars from file into array */
			fheader[8] = '\0';
		} else {
			ReportError(NULL, 0, 0);	/* couldn't open relocatable file */
			break;
		}

		if (strcmp(fheader, Z80objhdr) != 0) {  /* compare header of file */
			ReportError(objfilename, 0, 26);	/* not a object	file */
			fclose(z80asmfile);
			z80asmfile = NULL;
			break;
		}
		lowbyte	= fgetc(z80asmfile);
		highbyte = fgetc(z80asmfile);

		if (modulehdr->first ==	CURRENTMODULE) {        /* origin of first module */
			if (autorelocate)
				CURRENTMODULE->origin =	0;	/* ORG 0 on auto relocation */
			else {
				if (deforigin)
					CURRENTMODULE->origin =	EXPLICIT_ORIGIN;	/* use origin from command line	*/
				else {
					CURRENTMODULE->origin =	highbyte * 256U	+ lowbyte;
					if (CURRENTMODULE->origin == 65535U)
						DefineOrigin();	/* Define origin of first module from the keyboard */
				}
			}
			if (verbose == ON) printf("ORG address for code is %04X\n", CURRENTMODULE->origin);
		}
		fclose(z80asmfile);

		LinkModule(objfilename,	0);	/* link	code & read name definitions */
		free(objfilename);	/* release allocated file name */
		objfilename = NULL;

		CURRENTMODULE =	CURRENTMODULE->nextmodule;	/* get next module, if any */
	}
	while (CURRENTMODULE !=	lastobjmodule->nextmodule);	/* parse only object modules, not added	library	modules	*/

	if (verbose == ON) printf("Code size of linked modules is %d bytes\n", CODESIZE);

	if (ASMERROR ==	OFF) ModuleExpr();   /*	Evaluate expressions in	all modules */

	ReleaseLinkInfo();	/* Release module link information */

	fclose(errfile);
	if (TOTALERRORS	== 0) remove(errfilename);
	free(errfilename);
	errfilename = NULL;
}



int			LinkModule(char	*filename, long	fptr_base)
{
	long			fptr_namedecl, fptr_modname, fptr_modcode, fptr_libnmdecl;
	size_t			lowbyte, highbyte, size;
	int			flag = 0;

	z80asmfile = fopen(filename, "rb");	/* open	object file for	reading	*/
	fseek(z80asmfile, fptr_base + 10U, SEEK_SET);

	fptr_modname = ReadLong(z80asmfile);	/* get file pointer to module name */
	ReadLong(z80asmfile);	/* get file pointer to expression declarations */
	fptr_namedecl =	ReadLong(z80asmfile);	/* get file pointer to name declarations */
	fptr_libnmdecl = ReadLong(z80asmfile);	/* get file pointer to library name declarations */
	fptr_modcode = ReadLong(z80asmfile);	/* get file pointer to module code */

	if (fptr_modcode != -1)	{
		fseek(z80asmfile, fptr_base + fptr_modcode, SEEK_SET);	/* set file pointer to module code */
		lowbyte	= fgetc(z80asmfile);
		highbyte = fgetc(z80asmfile);
		size = lowbyte + highbyte * 256U;
		if (CURRENTMODULE->startoffset + size >	MAXCODESIZE) {
			ReportError(filename, 0, 12);
			return 0;
		} else
			fread(codearea + CURRENTMODULE->startoffset, sizeof(char), size, z80asmfile);	/* read	module code */

		if (CURRENTMODULE->startoffset == CODESIZE) CODESIZE +=	size;	    /* a new module has	been added */
	}
	if (fptr_namedecl != -1) {
		fseek(z80asmfile, fptr_base + fptr_namedecl, SEEK_SET);	/* set file pointer to point at	name
									 * declarations	*/
		if (fptr_libnmdecl != -1)
			ReadNames(fptr_namedecl, fptr_libnmdecl);	/* Read	symbols	until library declarations */
		else
			ReadNames(fptr_namedecl, fptr_modname);	/* Read	symbols	until module name */
	}
	fclose(z80asmfile);

	if (fptr_libnmdecl != -1) {
		if (library) {  /* search in libraries, if present */
			flag = LinkLibModules(filename,	fptr_base, fptr_libnmdecl, fptr_modname);	/* link	library	modules	*/
			if (!flag) return 0;
		}
	}
	return LinkTracedModule(filename, fptr_base);	/* Remember module for pass2 */
}



int			LinkLibModules(char *filename, long fptr_base, long nextname, long endnames)
{
	long			l;
	char		       *modname;

	do {
		z80asmfile = fopen(filename, "rb");	/* open	object file for	reading	*/
		fseek(z80asmfile, fptr_base + nextname,	SEEK_SET);	/* set file pointer to point at	library	name
									 * declarations	*/
		ReadName();	/* read	library	reference name */
		fclose(z80asmfile);

		l = strlen(line);
		nextname += 1 +	l;	/* remember module pointer to next name	in this	object module */
		if (FindSymbol(line, globalroot) == NULL) {
			modname	= AllocIdentifier((size_t) l + 1);
			if (modname == NULL) {
				ReportError(NULL, 0, 3);
				return 0;
			}
			strcpy(modname,	line);
			SearchLibraries(modname);	/* search name in libraries */
			free(modname);	/* remove copy of module name */
		}
	}
	while (nextname	< endnames);
	return 1;
}



void			SearchLibraries(char *modname)
{
	int			i;

	for (i = 0; i <	2; i++)	{       /* Libraries searched in max. 2 passes */
		while (CURRENTLIB != NULL) {
			if (SearchLibfile(CURRENTLIB, modname))	return;

			CURRENTLIB = CURRENTLIB->nextlib;
			if (CURRENTLIB != NULL)
				if (CURRENTLIB->nextobjfile != 8)
					CURRENTLIB->nextobjfile	= 8;	/* search at start of next lib */
		}

		/* last	library	read ... */
		CURRENTLIB = libraryhdr->firstlib;	/* start at the	beginning of the first module */
		CURRENTLIB->nextobjfile	= 8;		/* in the first	library	*/
	}
}



int			SearchLibfile(struct libfile * curlib, char *modname)
{
	long			currentlibmodule, modulesize, fptr_mname;
	int			flag;
	char		       *mname;

	z80asmfile = fopen(curlib->libfilename,	"rb");

	flag = 0;
	while (curlib->nextobjfile != -1) {     /* search name in all available library modules */
		do {                                                            /* point at first available module in library */
			fseek(z80asmfile, curlib->nextobjfile, SEEK_SET);	/* point at beginning of a module */
			currentlibmodule = curlib->nextobjfile;
			curlib->nextobjfile = ReadLong(z80asmfile);		/* get file pointer to next module in library */
			modulesize = ReadLong(z80asmfile);			/* get size of current module */
		}
		while (modulesize == 0 && curlib->nextobjfile != -1);

		if (modulesize != 0) {  /* found module name? */
			fseek(z80asmfile, currentlibmodule + 4 + 4 + 8 + 2, SEEK_SET);	/* point at module name	file
											 * pointer */
			fptr_mname = ReadLong(z80asmfile);	/* get module name file	pointer	*/
			fseek(z80asmfile, currentlibmodule + 4 + 4 + fptr_mname, SEEK_SET);	/* point at module name	*/
			mname =	ReadName();	/* read	module name */
			if (strcmp(mname, modname) == 0) {
				fclose(z80asmfile);
				return LinkLibModule(curlib, currentlibmodule +	4 + 4, modname);
			}
		}
	}

	fclose(z80asmfile);
	return flag;
}


int			LinkLibModule(struct libfile * library,	long curmodule,	char *modname)
{
	struct module	       *tmpmodule;
	int			flag;
	char		       *mname;

	tmpmodule = CURRENTMODULE;	/* remember current module */

	if ((CURRENTMODULE = NewModule()) != NULL) {
		mname =	AllocIdentifier(strlen(modname)	+ 1);	/* get a copy of module	name */
		if (mname != NULL) {
			strcpy(mname, modname);
			CURRENTMODULE->mname = mname;	/* create new module for library */
			CURRENTFILE = Newfile(NULL, library->libfilename);	/* filename for	'module' */

			if (verbose) printf("Linking library module <%s>\n", modname);
			flag = LinkModule(library->libfilename,	curmodule);	/* link	module & read names */
		} else {
			ReportError(NULL, 0, 3);
			flag = 0;
		}
	} else
		flag = 0;

	CURRENTMODULE =	tmpmodule;	/* restore previous current module */
	return flag;
}



char		       *ReadName(void)
{
	size_t	strlength;

	strlength = fgetc(z80asmfile);
	fread(line, sizeof(char), strlength, z80asmfile);	/* read	name */
	line[strlength]	= '\0';

	return line;
}



void			ModuleExpr(void)
{
	long			fptr_namedecl, fptr_modname, fptr_exprdecl, fptr_libnmdecl;
	long			fptr_base;
	struct linkedmod       *curlink;

	if (verbose) puts("Pass2...");

	curlink	= linkhdr->firstlink;
	do {
		CURRENTMODULE =	curlink->moduleinfo;
		fptr_base = curlink->modulestart;

		if ((z80asmfile	= fopen(curlink->objfilename, "rb")) !=	NULL) { /* open relocatable file for reading */
			fseek(z80asmfile, fptr_base + 10, SEEK_SET);	/* point at module name	pointer	*/
			fptr_modname = ReadLong(z80asmfile);		/* get file pointer to module name */
			fptr_exprdecl =	ReadLong(z80asmfile);		/* get file pointer to expression declarations */
			fptr_namedecl =	ReadLong(z80asmfile);		/* get file pointer to name declarations */
			fptr_libnmdecl = ReadLong(z80asmfile);		/* get file pointer to library name declarations */
		} else {
			ReportError(NULL, 0, 0);	/* couldn't open relocatable file */
			return;
		}

		if (fptr_exprdecl != -1) {
			fseek(z80asmfile, fptr_base + fptr_exprdecl, SEEK_SET);
			if (fptr_namedecl != -1)
				ReadExpr(fptr_exprdecl,	fptr_namedecl);	/* Evaluate until beginning of name
									 * declarations	*/
			else if	(fptr_libnmdecl	!= -1)
				ReadExpr(fptr_exprdecl,	fptr_libnmdecl);	/* Evaluate until beginning of library
										 * reference declarations */
			else
				ReadExpr(fptr_exprdecl,	fptr_modname);	/* Evaluate until beginning of module name */
		}
		fclose(z80asmfile);
		z80asmfile = NULL;

		curlink	= curlink->nextlink;
	}
	while (curlink != NULL);
}



void			CreateBinFile(void)
{
	char		*tmpstr;
	char		binfilenumber =	'0';
	FILE		*binaryfile;
	unsigned short	codeblock, offset;

	tmpstr = modulehdr->first->cfile->fname;	/* get source filename from first module */
	if (codesegment	== ON && CODESIZE > 16384)
		strcpy(tmpstr+strlen(tmpstr) - 4, segmbinext);		   /* replace '.asm' with '.bn0' extension */
	else
		strcpy(tmpstr+strlen(tmpstr) - 4, binext);		   /* replace '.asm' with '.bin' extension */

	binaryfile = fopen(tmpstr, "wb");		/* binary output to srcfilename.bin */
	if (binaryfile != NULL)	{
		if (autorelocate==ON &&	totaladdr!=0) {
			fwrite(reloc_routine, sizeof(char), sizeof_relocroutine, binaryfile); /* relocate routine */

			*(reloctable+0)	= (unsigned short) totaladdr % 256U;
			*(reloctable+1)	= (unsigned short) totaladdr / 256U;		/* total of relocation elements	*/
			*(reloctable+2)	= (unsigned short) sizeof_reloctable % 256U;
			*(reloctable+3)	= (unsigned short) sizeof_reloctable / 256U;	/* total size of relocation table elements */
			fwrite(	reloctable, 1U,	sizeof_reloctable+4, binaryfile);	/* write relocation table, inclusive 4 byte header */
			printf("Relocation header is %d bytes.\n", sizeof_relocroutine + sizeof_reloctable + 4);

			fwrite(codearea, sizeof(char), CODESIZE, binaryfile);		/* write code as one big chunk */
			fclose(binaryfile);
		} else if (codesegment == ON &&	CODESIZE>16384)	{
			fclose(binaryfile);
			offset = 0;
			do {
				codeblock = (CODESIZE/16384U) ?	16384U:	CODESIZE%16384U;
				CODESIZE -= codeblock;
				tmpstr[strlen(tmpstr)-1] = binfilenumber++;	  /* path code file with number	*/
				binaryfile = fopen(tmpstr, "wb");
				if (binaryfile != NULL)	{
					fwrite(codearea+offset,	sizeof(char), codeblock, binaryfile);	/* code	in 16K chunks */
					fclose(binaryfile);
				}
				offset += codeblock;
				
			} while(CODESIZE);
		} else {
			fwrite(codearea, sizeof(char), CODESIZE, binaryfile);	/* write code as one big chunk */
			fclose(binaryfile);
		}
		if (verbose) puts("Code generation completed.");
	} else
		ReportError(NULL, 0, 0);
}


void			CreateLib(void)
{
	struct stat		filestatus;
	int			objectfile;
	long			fptr;
	char		       *filebuffer, *fname;


	if (verbose) puts("Creating library...");

	CURRENTMODULE =	modulehdr->first;

	if ((errfilename = AllocIdentifier(strlen(libfilename) + 1)) !=	NULL) {
		strcpy(errfilename, libfilename);
		strcpy(errfilename + strlen(errfilename) - 4, errext);	/* overwrite '_lib' extension with '_err' */
	} else {
		ReportError(NULL, 0, 3);
		return;		/* No more room	*/
	}
	if ((errfile = fopen(errfilename, "w"))	== NULL) {      /* open error file */
		ReportError(NULL, 0, 0);			/* couldn't open relocatable file */
		free(errfilename);
		errfilename = NULL;
		return;
	}
	do {
		fname =	CURRENTFILE->fname;
		strcpy(fname + strlen(fname) - 4, objext);	/* overwrite '_asm' extension with '_obj' */
		if ((objectfile	= open(CURRENTFILE->fname, O_RDONLY | O_BINARY,	0)) != EOF) {
			fstat(objectfile, &filestatus);
			filebuffer = (char *) malloc((size_t) filestatus.st_size);
			if (filebuffer == NULL)	{
				ReportError(CURRENTFILE->fname,	0, 3);
				close(objectfile);
				break;
			}
			if (read(objectfile, filebuffer, filestatus.st_size) !=	filestatus.st_size) {
				ReportError(CURRENTFILE->fname,	0, 0);
				free(filebuffer);
				close(objectfile);
				break;
			}
			close(objectfile);

			if (memcmp(filebuffer, Z80objhdr, 8U) == 0) {
				printf("<%s> module at %04X.\n", CURRENTFILE->fname, ftell(libfile));
				if (CURRENTMODULE->nextmodule == NULL)
					WriteLong(-1, libfile);	/* this	is the last module */
				else {
					fptr = ftell(libfile) +	4 + 4;
					WriteLong(fptr + filestatus.st_size, libfile);	/* file	pointer	to next	module */
				}
				WriteLong(filestatus.st_size, libfile);	/* size	of this	module */
				fwrite(filebuffer, sizeof(char), (size_t) filestatus.st_size, libfile);	/* write module	to
													 * library */
				free(filebuffer);
			} else {
				free(filebuffer);
				ReportError(CURRENTFILE->fname,	0, 26);
				break;
			}
		} else {
			ReportError(CURRENTFILE->fname,	0, 0);
			break;
		}

		CURRENTMODULE =	CURRENTMODULE->nextmodule;
	}
	while (CURRENTMODULE !=	NULL);

	fclose(errfile);
	errfile	= NULL;
	if (ASMERROR ==	OFF) remove(errfilename);    /*	no errors */
	free(errfilename);
	errfilename = NULL;
}



int			LinkTracedModule(char *filename, long baseptr)
{
	struct linkedmod       *newm;
	char		       *fname;

	if (linkhdr == NULL) {
		if ((linkhdr = AllocLinkHdr()) == NULL)	{
			ReportError(NULL, 0, 3);
			return 0;
		} else {
			linkhdr->firstlink = NULL;
			linkhdr->lastlink = NULL;	/* Library header initialised */
		}
	}
	fname =	AllocIdentifier(strlen(filename) + 1);	/* get a copy module file name */
	if (fname != NULL)
		strcpy(fname, filename);
	else {
		ReportError(NULL, 0, 3);
		return 0;
	}

	if ((newm = AllocTracedModule()) == NULL) {
		free(fname);	/* release redundant copy of filename */
		ReportError(NULL, 0, 3);
		return 0;
	} else {
		newm->nextlink = NULL;
		newm->objfilename = fname;
		newm->modulestart = baseptr;
		newm->moduleinfo = CURRENTMODULE;	/* pointer to current (active) module structure	*/
	}

	if (linkhdr->firstlink == NULL)	{
		linkhdr->firstlink = newm;
		linkhdr->lastlink = newm;		/* First module	trace information */
	} else {
		linkhdr->lastlink->nextlink = newm;	/* current/last	linked module points now at new	current	*/
		linkhdr->lastlink = newm;		/* pointer to current linked module updated */
	}

	return 1;
}



long			ReadLong(FILE *	fileid)
{
#ifdef QDOS			/* high	byte, low byte order...	*/
	int			i;
	unsigned long		c, fptr	= 0;

	for (i = 1; i <= 3; i++) {
		fptr |=	fgetc(fileid) << 24;
		fptr >>= 8;
	}
	fptr |=	fgetc(fileid) << 24;

	return fptr;
#else				/* low byte, high byte order...	*/
	long			fptr;

	fread(&fptr, sizeof(long), 1, fileid);
	return fptr;
#endif
}


void			WriteLong(long fptr, FILE * fileid)
{
#ifdef QDOS			/* high	byte, low byte order...	*/
	int			i;

	for (i = 0; i <	4; i++)	{
		fputc(fptr & 255, fileid);
		fptr >>= 8;
	}
#else				/* low byte, high byte order...	*/
	fwrite(&fptr, sizeof(fptr), 1, fileid);
#endif
}

void			DefineOrigin(void)
{
	printf("ORG not yet defined!\nPlease enter as hexadecimal: ");
	scanf("%x", &modulehdr->first->origin);
}



void			CreateDeffile(void)
{
	char		       *globaldefname;

	/* use first module filename to	create global definition file */

	if ((globaldefname = AllocIdentifier(strlen(modulehdr->first->cfile->fname) + 1)) != NULL) {
		strcpy(globaldefname, modulehdr->first->cfile->fname);
		strcpy(globaldefname + strlen(globaldefname) - 4, defext);	/* overwrite '_asm' extension with
										 * '_def' */

		if ((deffile = fopen(globaldefname, "w")) == NULL) {    /* Create DEFC file with global label
									 * declarations	*/
			ReportError(NULL, 0, 0);
			globaldef = OFF;
		}
	} else {
		ReportError(NULL, 0, 3);
		globaldef = OFF;
	}

	free(globaldefname);
	globaldefname =	NULL;
}



void			WriteMapFile(void)
{
	avltree		       *maproot	= NULL,	*newmaproot = NULL;
	struct module	       *cmodule;
	char		       *mapfilename;

	cmodule	= modulehdr->first;	/* begin with first module */

	if ((mapfilename = AllocIdentifier(strlen(cmodule->cfile->fname) + 1)) != NULL)	{
		strcpy(mapfilename, cmodule->cfile->fname);
		strcpy(mapfilename + strlen(mapfilename) - 4, mapext);	/* overwrite '_asm' extension with '_map' */
	} else {
		ReportError(NULL, 0, 3);
		return;		/* No more room	*/
	}

	if ((mapfile = fopen(mapfilename, "w"))	!= NULL) {      /* Create MAP file */
		if (verbose) puts("Creating map...");
		do {
			move(&cmodule->localroot, &maproot, (int (*)())	cmpidstr);  /* move all	local address symbols alphabetically */
			cmodule	= cmodule->nextmodule;	/* alphabetically */
		}
		while (cmodule != NULL);

		move(&globalroot, &maproot, (int (*)())	cmpidstr);  /* move all	global address symbols alphabetically */

		if (maproot == NULL)
			fputs("None.\n", mapfile);
		else {
			inorder(maproot, (void (*)()) WriteMapSymbol);		/* Write map symbols alphabetically */
			move(&maproot, &newmaproot, (int (*)())	cmpidval);	/* then	re-order symbols numerically */
			fputs("\n\n", mapfile);
			inorder(newmaproot, (void (*)()) WriteMapSymbol);	/* Write map symbols numerically */
			deleteall(&newmaproot, (void (*)()) FreeSym);		/* then	release	all map	symbols	*/
		}
		fclose(mapfile);
	} else {
		ReportError(NULL, 0, 0);
		return;
	}

	free(mapfilename);
}


void			WriteMapSymbol(symbol *mapnode)
{
	int			tabulators, space;

	if (mapnode->type & SYMADDR) {
		fprintf(mapfile, "%s", mapnode->symname);
		space =	COLUMN_WIDTH - strlen(mapnode->symname);
		for (	tabulators = space % TAB_DIST ?	space /	TAB_DIST + 1 : space / TAB_DIST;
			tabulators > 0;	tabulators--)
			fputc('\t', mapfile);

		if (autorelocate)
			fprintf(mapfile, "= %04X, ", sizeof_relocroutine + sizeof_reloctable + 4 + mapnode->symvalue);
		else
			fprintf(mapfile, "= %04X, ", mapnode->symvalue);
		if (mapnode->type & SYMLOCAL)
			fputc('L', mapfile);
		else
			fputc('G', mapfile);
		fprintf(mapfile, ": %s\n", mapnode->owner->mname);
	}
}


void			WriteGlobal(symbol *node)
{
	int			tabulators, space;

	if ((node->type	& SYMTOUCHED) && (node->type & SYMADDR)	&&
	    (node->type	& SYMXDEF) && !(node->type & SYMDEF)) {
		/* Write only global definitions - not library routines	*/
		fprintf(deffile, "DEFC %s", node->symname);
		space =	COLUMN_WIDTH - 5 - strlen(node->symname);
		for (tabulators	= space	% TAB_DIST ? space / TAB_DIST +	1 : space / TAB_DIST; tabulators > 0; tabulators--)
			fputc('\t', deffile);
		fprintf(deffile, "= $%04X; ", node->symvalue + modulehdr->first->origin	+ CURRENTMODULE->startoffset);
		fprintf(deffile, "Module %s\n",	node->owner->mname);
	}
}


void			ReleaseLinkInfo(void)
{
	struct linkedmod       *m, *n;

	if (linkhdr == NULL) return;

	m = linkhdr->firstlink;
	do {
		if (m->objfilename != NULL)
			free(m->objfilename);
		n = m->nextlink;
		free(m);
		m = n;
	}
	while (m != NULL);

	free(linkhdr);
	linkhdr	= NULL;
}


struct linklist	       *AllocLinkHdr(void)
{
	return (struct linklist	*) malloc(sizeof(struct	linklist));
}


struct linkedmod       *AllocTracedModule(void)
{
	return (struct linkedmod *) malloc(sizeof(struct linkedmod));
}
