/*
 * File: main.c
 *
 * Main file for IMP80 compiler
 *
 * Bob Eager   August 2002
 *
 */

#include "imp.h"
#include "opcode.h"
#include "global.h"
#include "global2.h"
#include "lput.h"
#include "sntabs.h"
#include "snames.h"
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdarg.h>

#ifndef LIBHASSTRLWR
#include <ctype.h>
char *strlwr(char *inplace)
{
  int c;
  char *orig = inplace;
  while ((c = *inplace) != '\0') {
    if (isalpha(c) && isupper(c)) c = tolower(c);
    *inplace++ = c;
  }
  return(orig);
}
#endif

#define	DEFFILESIZE	64000		/* Default input file size */
#define	DEFWORK		128		/* Default workfile size (K) */
#define	MAXFILE		63		/* Maximum length of filename */
#define	MAXWORK		256		/* Maximum workfile size (K) */
#define	MINWORK		128		/* Minimum workfile size (K) */

#define	PATHSEP1	'\\'
#define	PATHSEP2	'/'
#ifdef	__OS2__
#define	PATHSEP		PATHSEP1
#else
#define	PATHSEP		PATHSEP2
#endif

/* Forward references */

static	VOID	error(PUCHAR, ...);
static	FILE	*openio(PUCHAR, PUCHAR);
static	VOID	usage(VOID);


INT main(INT argc, PUCHAR argv[])
{	PUCHAR argp;			/* Used to process arguments */
	clock_t cptime;			/* Time at start of compilation */
	LONG filesize;			/* Size of input file (bytes) */
	INT i, j, k;			/* Miscellaneous counters */
	UCHAR listfile[MAXFILE];	/* Name of listing file */
	UCHAR modnwork[MAXFILE];	/* Module name work area */
	PUCHAR modulename = modnwork;	/* Module name */
	UCHAR objfile[MAXFILE];		/* Name of object file */	
	FILE *objfp;			/* File pointer for object file */
	PUCHAR sp;			/* Miscellaneous pointer */
	INT snum;			/* Special name declaration counter */
	UCHAR sourcefile[MAXFILE]	;/* Name of source file */
	struct stat stbuf;		/* Contains source file info */
	INT maxulab;
	INT plabel;
	INT stlimit;
	INT glaca;
	INT glacabuf;
	INT glacurr;
	INT ustptr;
	INT auxst;
	INT iocpdisp;
	INT sstl;
	INT levelinf;

#if	0
	INT bfflag;
	INT bimstr;
	INT cdcount;
	INT contlab;
	INT crefhead;
	INT exitlab;
	INT ldptr;
	INT n;
	INT nmax;
	INT rbase;
	INT rectb;
	INT rlevel;
	INT sflabel;
#endif

	progname = argv[0];

	/* Massage the program name into an acceptable form */

	sp = strrchr(progname, PATHSEP);
	if(sp != (PUCHAR) NULL) progname = ++sp;
	(VOID) strlwr(progname);

	cptime = clock();

#if 0
	/* Initialise memory pointers in case of early exit */

	aslist = (LISTF *) NULL;
	cc = (PUCHAR) NULL;
	display = (PINT) NULL;
	dvheads = (PINT) NULL;
	lett = (PUCHAR) NULL;
	mainep = (PUCHAR) NULL;
	sfs = (PINT) NULL;
	tags = (UINT *) NULL;
	tline = (PUCHAR) NULL;
	wkfilead = (PUCHAR) NULL;
	word = (PINT) NULL;
#endif

	/* Set option defaults */

	dcomp = NO;
	faulty = 0;
	freeformat = NO;
	list = NO;
	parmarr = YES;
	parmchk = YES;
	parmdbug = NO;
	parmdiag = YES;
	parmlet = NO;
	parmline = YES;
	parmmax = NO;
	parmopt = NO;
	parmprof = NO;
	parmtrace = YES;
	parmx = NO;
	parmy = NO;
#if	DEBUG
	smap = NO;
#endif
	stack = NO;
	listfile[0] = '\0';
	objfile[0] = '\0';
	sourcefile[0] = '\0';
	wkfilek = DEFWORK;

	/* Process arguments */

	i = 1;
	while(i < argc) {
		argp = argv[i++];
		if(*argp == '-') {	/* Flag */
			argp++;
			while(*argp != '\0') {
				switch(*argp++) {
					case 'a':	/* NOARRAY */
						parmarr = NO;
						break;

					case 'c':	/* CODE */
						dcomp = YES;
						break;

					case 'd':	/* NODIAG */
						parmdiag = NO;
						break;

					case 'f':	/* FREE */
						freeformat = YES;
						break;

					case 'g':	/* DEBUG */
						parmdbug = YES;
						break;

					case 'i':	/* LET */
						parmlet = YES;
						break;

					case 'k':	/* NOCHECK */
						parmchk = NO;
						break;

					case 'l':	/* Listing file */
						if(listfile[0] != '\0')
							error("listing file"
							" specified more than"
							" once");
						(VOID) strcpy(listfile, argp);
						list = YES;
						argp += strlen(argp);
						break;

					case 'm':	/* MAXDICT */
						parmmax = YES;
						break;

					case 'n':	/* NOLINE */
						parmline = NO;
						break;

					case 'o':
						if(*argp != '\0') {
							/* Object file */
							if(objfile[0] != '\0')
								error("object file specified more than once");
							(VOID) strcpy(objfile, argp);
							argp += strlen(argp);
						} else {
							parmopt = YES;
						}
						break;

					case 'p':	/* PROFILE */
						parmprof = YES;
						break;

					case 's':	/* STACK */
						stack = YES;
						break;

					case 't':	/* NOTRACE */
						parmtrace = NO;
						parmdiag = NO;
						break;

					case 'w':	/* Workfile size */
						if(*argp == '\0')
							wkfilek = DEFWORK;
						else {
							INT temp = atoi(argp);

							if(temp < 0)
								temp = INT_MAX;
							wkfilek = temp;
							if((wkfilek < MINWORK) || (wkfilek > MAXWORK))
								error("workfile size must be between %d and %d", MINWORK, MAXWORK);
							argp += strlen(argp);
						}
						break;

					case 'x':	/* PARMX */
						parmx = YES;
						break;

					case 'y':	/* PARMY */
						parmy = YES;
						break;

#if	DEBUG
					case 'z':	/* PARMZ */
						smap = YES;
						break;
#endif

					default:
						error("unknown option '%c'", argp[-1]);

				}
			}
		} else {		/* Is an input file */
			if(sourcefile[0] != '\0')
				error("source file already specified");
			(VOID) strcpy(sourcefile, argp);
		}
	}
	if((parmtrace == NO) && (parmopt == NO))
		parmtrace = YES;	/* Allow NOTRACE only with OPT */

	if(parmopt == YES) {
		parmarr = NO;
		parmopt = NO;
		parmline = NO;
		parmchk = NO;
		parmdiag = NO;
	}

	/* Open source file */

	if(sourcefile[0] == '\0') error("no source file specified");
	(VOID) strlwr(sourcefile);
	sp = strrchr(sourcefile, '.');
	if(sp == (PUCHAR) NULL) (VOID) strcat(sourcefile, ".imp");
	infp = openio(sourcefile, "r");
	filesize = -1;			/* In case of stat error */
	if(stat(sourcefile, &stbuf) == 0) filesize = stbuf.st_size;
	if(filesize < 0) filesize = DEFFILESIZE;

	/* Open object file */

	(VOID) strlwr(objfile);
	if(objfile[0] == '\0') {	/* Derive name from source file */
		(VOID) strcpy(objfile, sourcefile);
		sp = strrchr(objfile, '.');
		if(sp != (PUCHAR) NULL) *sp = '\0';
					/* Truncate extension */
	}
	sp = strrchr(objfile, '.');
	if(sp == (PUCHAR) NULL)	/* Default to .OBJ */
		(VOID) strcat(objfile, ".obj");
	objfp = openio(objfile, "wb");

	/* Open listing file, or use NUL */

	if(list == YES) {
		(VOID) strlwr(listfile);
		if(listfile[0] == '\0') {
					/* Derive name from source file */
			(VOID) strcpy(listfile, sourcefile);
			sp = strrchr(listfile, '.');
			if(sp != (PUCHAR) NULL) *sp = '\0';
					/* Truncate extension */
		}
		sp = strrchr(listfile, '.');
		if(sp == (PUCHAR) NULL)	/* Default to .LST */
			(VOID) strcat(listfile, ".lst");
	} else (VOID) strcpy(listfile, "/dev/null");
	listfp = openio(listfile, "w");

	/* Generate module name. This is derived from the base
	   source file name. */

	sp = strrchr(sourcefile, PATHSEP1);
	if(sp == (PUCHAR) NULL)
		sp = strrchr(sourcefile, PATHSEP2);
					/* Strip any directory path */
	if(sp == (PUCHAR) NULL) sp = sourcefile;
	else sp++;
	strcpy(modulename, sp);		/* Strip filename extension */
	sp = strrchr(modulename, '.');
	if(sp != (PUCHAR) NULL) *sp = '\0';
#ifdef __OS2__
	sp = strchr(modulename, ':');	/* Strip drive name */
	if(sp != (PUCHAR) NULL) modulename = ++sp;
#endif

	/* Set up compilation options and tables */

	wkfilead = (PUCHAR) xalloc(wkfilek*1024);
	nnames = 255;
	if(filesize > 10000) nnames = 511;
	if(filesize > 32000) nnames = 1023;
	if((filesize > 262144) || (parmmax == YES) || (wkfilek > 512))
		nnames = 2047;
	asl = 3*nnames;
	if((asl > 4095) && (wkfilek < 512) && (parmmax == NO))
		asl = 4095;		/* Stay within 128K auxstack (!!) */

	arsize = wkfilek*768 - 300;
	aslist = (LISTF *) xalloc(sizeof(LISTF)*(asl+1));
	word = (PINT) xalloc(sizeof(INT)*(nnames+1));
	tags = (PUINT) xalloc(sizeof(INT)*(nnames+1));
	dvheads = (PINT) xalloc(sizeof(INT)*13);
	sfs = (PINT) xalloc(sizeof(INT)*(MAXLEVELS+1));
	display = (PINT) xalloc(sizeof(INT)*(MAXLEVELS+1));
	mainep = xalloc(32);

	/* Start of compilation */

	a = wkfilead + 256*wkfilek;	/* a[] is 25% into workfile */

	/* Initialise compiler scalars and arrays */

	dsize = 7*nnames;
	ccsize = 256*(wkfilek-1);
	if(parmmax == YES) dsize += nnames;
	lett = (PUCHAR) xalloc(dsize+21);
	tline = (PUCHAR) xalloc(276);
	tline = &tline[20];		/* Make array -20:... */

	cc = (PUCHAR) xalloc(ccsize);
	imax = INT_MAX;
	plabel = 24999;
	lett[0] = 0;
#if	0
	n = 12;
#endif
	maxulab = nnames+16384;		/* Largest valid user label */
	glacurr = 0;
	glaca = FIXEDGLALEN;
	glacabuf = glaca;
#if	0
	sflabel = 20999;
	exitlab = 0;
	contlab = 0;
	nmax = 0;
#endif
	ppcurr = 0;
	cabuf = 0;
	oldline = 0;
	compiler = NO;
	ustptr = 0;
	level = 0;
	ca = 0;
	lastat = 0;
#if	0
	bfflag = 0;
	ldptr = 0;
#endif
	cprmode = 0;
	next = 1;
	iocpdisp = 0;
	auxst = 0;
#if	0
	crefhead = 0;
	rbase = 10;
	rlevel = 0;
	rectb = 0;
#endif
	logepdisp = 0;
	expepdisp = 0;
	strlink = 0;
	ihead = 0;
	idepth = 0;
	sstl = 0;
	levelinf = 0;
#if	0
	cdcount = 0;
	bimstr = 0;
#endif
	strcpy(mainep, "S#GO");		/* Default main entry */
	dictbase = &lett[0];
	{	LPUT0 l0;

		(VOID) strcpy(l0.compiler_id, COMPILER);
		l0.version = (RELEASE << 16) | EDIT;
		(VOID) strcpy(l0.compiler_ladate, LADATE);
		lput(0,
		     (INT) modulename,
		     (INT) objfp,
		     (INT) &l0);
	}
	stlimit = 0x1f000;
	if(list == YES) {
		fprintf(listfp,
		"\n\n\n           80x86 Imp80 compiler release %d.%d"
		" Version %s\n\n\n",
		RELEASE, EDIT, LADATE);
		fprintf(listfp, "%5d %5d\n", nnames, asl);
	}
	aslwarn = 0;
	aslcurbtm = asl - 240;
	const_limit = sizeof(LISTF)*aslcurbtm - 8;
				/* Top of constant list - bottom of asl */
	for(i = aslcurbtm; i <= asl - 1; i++) aslist[i+1].link = i;
	aslist[0].s1 = -1;
	aslist[0].s2 = -1;
	aslist[0].s3 = -1;
	aslist[0].link = 0;
	for(i = 0; i <= nnames; i++) {
		word[i] = 0;
		tags[i] = 0;
	}
	for(i = 0; i <= 12; i++) dvheads[i] = 0;
	for(i = 0; i <= MAXLEVELS; i++) sfs[i] = 0;

	/* Now declare the special names which are in the array 'ilett' */

	snum = 0;			/* Number of first name */
	k = 0;				/* Index of first name */
	i = ilett[k];			/* Length of first name */
	while(i < 255) {
		for(j = 1; j <= i; j++) cc[j] = ilett[k+j];
		cc[i+1] = ';';		/* Copy and terminate */
		r = 2;			/* Dummy place in 'a' */
		q = 1;			/* Start of name in 'cc' */
		pname(1);		/* Declare new name */
		push((PINT) (PVOID) &tags[lastname],
		     (SNPT<<16) | 0x8000, 0, snum << 16);
		snum++;			/* Increment special name number */
		k = k + i + 1;		/* Index of next name */
		i = ilett[k];
	}

	pass1();			/* Do syntactic analysis */

	if(level > 1) fault(15, level-1, 0);	/* %ENDs are missing */
	if(faulty == 0) {
		cptime = clock() - cptime;	/* 100ths of secs used */
		fprintf(listfp,
			"\n  %d lines analysed in %ld0 msecs - size = %d",
			line, cptime, p1size);
		if((line > 90) && (list == YES)) fputc('\f', listfp);
		else fputc('\n', listfp);
	} else {
		fputs("\n  Code generation not attempted\n", listfp);
		iexit(EXIT_FAILURE);
	}

#if 0
	pass2();
#endif

	iexit(EXIT_SUCCESS);
	return(EXIT_SUCCESS);
}


/*
 * Routine to output an error message and stop.
 *
 */

static VOID error(PUCHAR message, ...)
{	va_list ap;

	fprintf(stderr, "%s: ", progname);

	va_start(ap, message);
	vfprintf(stderr, message, ap);
	va_end(ap);

	fputc('\n', stderr);
	faulty = -1;
	usage();
}

/*
 * Routine to perform tidy exit.
 * Returns any allocated memory.
 *
 */

VOID iexit(INT n)
{	if(faulty == 0) {
		fputs("Compilation successful\n", stderr);
	} else {
		if(faulty > 0)
			fprintf(stderr, "Program contains %d fault%s\n",
				faulty, faulty == 1 ? "" : "s");
	}
	exit(n);
}


/*
 * Function to open a file in a specified mode
 * Does not return if there is a failure, but outputs an error
 * message and exits
 *
 */

static FILE *openio(PUCHAR file, PUCHAR mode)
{	FILE *fp;

	fp = fopen(file, mode);
	if(fp == (FILE *) NULL) {
		fprintf(stderr, "%s: cannot open '%s'\n", progname, file);
		iexit(EXIT_FAILURE);
	}
	return(fp);
}


/*
 * Routine to output brief usage information, then exit.
 *
 */

static VOID usage(VOID)
{	fprintf(stderr, "%s: usage: %s [flags] file\n", progname, progname);
	iexit(EXIT_SUCCESS);
}

/*
 * End of file: main.c
 *
 */
