 /* $Header:asmain.c 12.0$ */
 /* $ACIS:asmain.c 12.0$ */
 /* $Source: /ibm/acis/usr/src/bin/as_ca/RCS/asmain.c,v $ */
#ifndef lint
static char rcsid[] = "$Header:asmain.c 12.0$";
#endif
/* $Source: /ibm/acis/usr/src/bin/as_ca/RCS/asmain.c,v $ */
/*
 *	Copyright (c) 1982 Regents of the University of California
 */
#ifndef lint
static char sccsid[] = "@(#)asmain.c 4.13 6/30/83";
#endif not lint

#include <stdio.h>
#include <ctype.h>
#include <signal.h>

#include "as.h"
#include "assyms.h"
#include "asscan.h"
#include "asexpr.h"

#include <sys/stat.h>

#define	unix_lang_name "VAX/UNIX Assembler V6/30/83 4.13"
/*
 *	variables to manage reading the assembly source files
 */
char	*dotsname;	/*the current file name; managed by the parser*/
int	lineno;		/*current line number; managed by the parser*/
char	**innames;	/*names of the files being assembled*/
int	ninfiles;	/*how many interesting files there are*/
/*
 *	Flags settable from the argv process argument list
 */
int	silent = 0;	/*don't complain about any errors*/
int	savelabels = 0;	/*write the labels to the a.out file*/
int 	d124 = 8;	/*default allocate 4 bytes for unknown pointers*/
int	maxalign = 2;	/*default .align maximum*/
int	anyerrs = 0;	/*no errors yet*/
int	anywarnings=0;	/*no warnings yet*/
int	orgwarn = 0;	/*Bad origins*/
int	d4error = 0;	/*d4 and get $data fails*/
int	dseterr = 0;	/*.set value changes*/
int	passno = 1;	/* current pass*/
int	jxxxJUMP = 0;	/* in jxxxes that branch too far, use jmp instead of brw */
int	strictbounds = 0;
			/* Check that displacements are strictly within
			 * limits: don't accept  0xffff if -1 is required,
			 * and vice versa.  Otherwise accept quietly any
			 * displacement in [-32k, 65k).
			 */
int	readonlydata = 0;	/* initialzed data -> text space */

int	nGHnumbers = 0;		/* GH numbers used */
int	nGHopcodes = 0;		/* GH opcodes used */
int	nnewopcodes = 0;	/* new opcodes used */

int	showcode = 0;		/* show hex code generated */ /*SFZ*/
#ifdef DEBUG
int 	debug = 0;
int	toktrace = 0;
#endif

int	useVM =	0;
int	ltorg_noalign = 0;	/* don't align before literals */

char	*endcore;	/*where to get more symbol space*/

/*
 *	Managers of the a.out file.
 */
struct	exec	hdr;
#define	MAGIC	0407
u_long	tsize;		/* total text size */
u_long	dsize;		/* total data size */
u_long	datbase;	/* base of the data segment */
u_long	trsize;		/* total text relocation size */
u_long	drsize;		/* total data relocation size */

/*
 *	Information about the current segment is accumulated in
 *	usedot; the most important information stored is the
 *	accumulated size of each of the text and data segments
 *
 *	dotp points to the correct usedot expression for the current segment
 */
struct	exp	usedot[NLOC+NLOC];	/* info about all segments */
struct	exp	*dotp;			/* data/text location pointer */
struct	basereg	usebreg[NLOC+NLOC];	/* basereg info for each segment */
/*
 *	The inter pass temporary token file is opened and closed by stdio, but
 *	is written to using direct read/write, as the temporary file
 *	is composed of buffers exactly BUFSIZ long.
 */
FILE	*tokfile;			/* interpass communication file */
char	tokfilename[TNAMESIZE];
/*
 *	The string file is the string table
 *	cat'ed to the end of the built up a.out file
 */
FILE	*strfile;			/* interpass string file */
char	strfilename[TNAMESIZE];
int	strfilepos = 0;			/* position within the string file */
/*
 *	a.out is created during the second pass.
 *	It is opened by stdio, but is filled with the parallel
 *	block I/O library
 */
char	*outfile = "a.out";
FILE	*a_out_file;			
off_t	a_out_off;			/* cumulative offsets for segments */
/*
 *	The logical files containing the assembled data for each of
 *	the text and data segments are
 *	managed by the parallel block I/O library.
 *	a.out is logically opened in many places at once to
 *	receive the assembled data from the various segments as
 *	it all trickles in, but is physically opened only once
 *	to minimize file overhead.
 */
BFILE	*usefile[NLOC+NLOC];		/* text/data files */
BFILE	*txtfil;			/* current text/data file */
/*
 *	Relocation information is accumulated seperately for each
 *	segment.  This is required by the old loader (from BTL),
 *	but not by the new loader (Bill Joy).  
 *
 *	However, the size of the relocation information can not be computed
 *	during or after the 1st pass because the ''absoluteness' of values
 *	is unknown until all locally declared symbols have been seen.
 *	Thus, the size of the relocation information is only
 *	known after the second pass is finished.
 *	This obviates the use of the block I/O
 *	library, which requires knowing the exact offsets in a.out.
 *
 *	So, we save the relocation information internally (we don't
 *	go to internal files to minimize overhead).
 *
 *	Empirically, we studied 259 files composing the system,
 *	two compilers and a compiler generator: (all of which have
 *	fairly large source files)
 *	
 *	Number of files = 259
 *		Number of non zero text reloc files: 233
 *		Number of non zero data reloc files: 53
 *	Average text relocation = 889
 *	Average data relocation = 346
 *	Number of files > BUFSIZ text relocation = 71
 *	Number of files > BUFSIZ data relocation = 6
 *	
 *	For compiled C code, there is usually one text segment and two
 *	data segments; we see that allocating our own buffers and
 *	doing our internal handling of relocation information will,
 *	on the average, not use more memory than taken up by the buffers
 *	allocated for doing file I/O in parallel to a number of file.
 *	
 *	If we are assembling with the -V option, we
 *	use the left over token buffers from the 2nd pass,
 *	otherwise, we create our own.
 *
 *	When the 2nd pass is complete, closeoutrel flushes the token
 *	buffers out to a BFILE.
 *
 *	The internals to relbufdesc are known only in assyms.c
 *
 *	outrel constructs the relocation information.
 *	closeoutrel flushes the relocation information to relfil.
 */
struct	relbufdesc	*rusefile[NLOC+NLOC];	
struct	relbufdesc 	*relfil;	/* un concatnated relocation info */
BFILE	*relocfile;			/* concatnated relocation info */
/*
 *	Once the relocation information has been written,
 *	we can write out the symbol table using the Block I/O
 *	mechanisms, as we once again know the offsets into
 *	the a.out file.
 *
 *	We use relfil to output the symbol table information.
 */
char	*tmpdirprefix = "/tmp/";
int delexit();

main(argc, argv)
	int	argc;
	char 	**argv;
{
	char	*sbrk();
	int	rc;

	tokfilename[0] = 0;
	strfilename[0] = 0;
	endcore = sbrk(0);

	argprocess(argc, argv);		/* process argument lists */
	if (anyerrs) exit(1);

	initialize();			/* install all known instructions
					   in the symbol table and build
					   expression parser accelerator
					   token sets */
	zeroorigins();			/* set origins to zero */
	zerolocals();			/* fix local label counters */

	i_pass1();			/* open temp files, etc */
	pass1();			/* first pass through .s files */
	testlocals();			/* check for undefined locals */
	if (anyerrs) delexit();

	pass1_5();			/* resolve jxxx */
	if (anyerrs) delexit();

	open_a_out();			/* open a.out */
	roundsegments();		/* round segments to FW */
	build_hdr();			/* build initial header, and output */
	
	i_pass2();			/* reopen temporary file, etc */
	pass2();			/* second pass through the virtual .s */
	anyerrs -= d4error;
	if (anyerrs || d4error) delexit();

	fillsegments();			/* fill segments with 0 to FW */
	reloc_syms();			/* dump relocation and symbol table */
#ifdef	DEBUG
/*	if (!toktrace) /* temp for debug.. SFZ */
#endif
	delete();  			/* remove tmp file */
	bflush();			/* close off block I/O view of a.out */
	fix_a_out();			/* add in text and data reloc counts */

	if (anyerrs == 0 && orgwarn)
		yyerror("Caution: absolute origins.\n");

	if (nGHnumbers)
		yywarning("Caution: H format floating point numbers");
	if (nGHopcodes)
		yywarning("Caution: H format floating point operators");
	if (nnewopcodes)
		yywarning("Caution: New Opcodes");
	if (nGHnumbers || nGHopcodes || nnewopcodes)
		yywarning("These are not defined for all implementations of the VAX architecture.\n");

	rc = (anyerrs != 0);
	if (d4error) rc |= 4;
	if (dseterr && d124&BROPT) rc |= 2;
	exit(rc);
}

argprocess(argc, argv)
	int	argc;
	char	*argv[];
{
	register	char	*cp;

	ninfiles = 0;
	silent = 0;
#ifdef DEBUG
	debug = 0;
#endif
	innames = (char **)ClearCalloc(argc+1, sizeof (innames[0]));
	dotsname = "<argv error>";
	while (argc > 1) {
		if (argv[1][0] != '-')
			innames[ninfiles++] = argv[1];
		else {
			cp = argv[1] + 1;
				switch(*cp++){
				case '\0':
					break;
			/*
			 *	Throw away single minus signs, so
			 *	that make scripts for the PDP 11 assembler work
			 *	on this assembler too
			 */
				 case 's':
					if (0 == strcmp(cp,"trict")) {
					    strictbounds=1;
					    break;
					}
				 default:
					yyerror("Unknown flag: %s", cp-1);
					break;
				 case 'v':
					selfwhat(stdout);
					exit(1);
				 case 'A':
					ltorg_noalign++;
					break;
				 case 'd':
					d124 = *cp - '0';
					if ( (d124 < 1) || (d124 > 8) ) {
						yyerror("-d[1-8] only");
						exit(1);
					}
					break;
				 case 'a':
					maxalign = atoi(cp);
					if ( (maxalign > 16) || (maxalign < 0)){
						yyerror("-A: 0<=align<=16");
						exit(1);
					}
					break;
				 case 'o':
					if (argc < 3){
						yyerror("-o what???");
						exit(1);
					}
					outfile = argv[2];
				   bumpone:
					argc -= 2;
					argv += 2;
					goto nextarg;

				 case 't':
					if (argc < 3){
						yyerror("-t what???");
						exit(1);
					}
					tmpdirprefix = argv[2];
					goto bumpone;
				 
				 case 'V':
					useVM = 1;
					break;
				 case 'W':
					silent = 1;
					break;
				 case 'L':
					savelabels = 1;
					break;
				 case 'J':
					jxxxJUMP = 1;
					break;
#ifdef DEBUG
				 case 'D':
					debug = 1;
					break;
				 case 'T':
					toktrace = 1;
					break;
#endif
				 case 'R':
					readonlydata = 1;
					break;
				 case 'S':
					showcode = 1;
					break;
				}	/*end of the switch*/
		}	/*end of a flag argument*/
		--argc; ++argv;
	   nextarg:;
	}
	/* innames[ninfiles] = 0; */
}
/*
 *	poke through the data space and find all rcs identifiers.
 *	We assume:
 *	a) that extern char **environ; is the first thing in the bss
 *	segment (true, if one is using the new version of cmgt.crt0.c)
 *	b) that the rcsid's have not been put into text space.
 */
selfwhat(place)
	FILE	*place;
{
	extern	char **environ;
	register	char	*ub;
	register	char *cp;
	register	char	*pat;
	char	*sbrk();

	for (cp = (char *)&environ, ub = sbrk(0); cp < ub; cp++){
		if (strncmp(cp,"$Header:",8)) continue;
		fputc('\t', place);
		for (cp += 8; cp < ub; cp++){
			if (*cp == 0) break;
			if (*cp == '$') break;
			if (*cp == '\n') break;
			fputc(*cp, place);
		}
		fputc('\n', place);
	}
}

initialize()
{
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, delexit);
	/*
	 *	Install symbols in the table
	 *	These routines are in assyms.c.
	 */
	symtabinit();			/* get the first strpool storage area
					   and the first part of the hash table */
	syminstall();			/* install all known instructions in the
					   symbol table */
	/*
	 *	Build the expression parser accelerator token sets
	 *	This routine is in asexpr.c.
	 */
	buildtokensets();		
}

zeroorigins()
{
	register	int	locindex;
	/*
	 *	Mark usedot: the first NLOC slots are for named text segments,
	 *	the next for named data segments.
	 */
	for (locindex = 0; locindex < NLOC; locindex++){
		usedot[locindex].e_xtype = XTEXT;
		usedot[NLOC + locindex].e_xtype = XDATA;
		usedot[locindex].e_xvalue = 0;
		usedot[NLOC + locindex].e_xvalue = 0;
	}
}

zerolocals()
{
	register	int	i;

	for (i = 0; i <= 10; i++) {
		lgensym[i] = 1;
		genref[i] = 0;
	}

	litptr = litpool;
}

i_pass1()
{
	FILE	*tempopen();
	if (useVM == 0)			/* if not using virtual memory for
					   intermediate storage, open the
					   token file */
		tokfile = tempopen(tokfilename, "T");
	strfile = tempopen(strfilename, "S");
	/*
	 *	write out the string length.
	 *	This will be overwritten when the
	 *	strings are tacked onto the growing a.out file
	 */
	strfilepos = sizeof(int);
	fwrite(&strfilepos, sizeof(int), 1, strfile);

	inittokfile();			/* in asscan1.c - set all the token
					   file pointers to zero */
	initijxxx();			/* in asjxxx.c - initialize variables */
}

FILE *tempopen(tname, part)
	char	*tname;
	char	*part;
{
	FILE	*file;
	sprintf(tname, "%s%sas%s%05d",
		tmpdirprefix,
		(tmpdirprefix[strlen(tmpdirprefix)-1] != '/') ? "/" : "",
		part,
		getpid());
	file = fopen(tname, "w");	/* open the file for writing */
	if (file == NULL) {
		yyerror("Bad pass 1 temporary file for writing %s", tname);
		delexit();
	}
	return(file);
}

pass1()
{
	register	int	i;

	passno = 1;
	dotp = &usedot[0];
	txtfil = (BFILE *)0;
	relfil = (struct relbufdesc *)0;

	if (ninfiles == 0){		/*take the input from stdin directly*/
		lineno = 1;
		dotsname = "<stdin>";

		yyparse();
	} else {		
		for (i = 0; i < ninfiles; i++){
					/* open each of the source files in
					   turn for reading */
			new_dot_s(innames[i]);
					/* in asscan1.c - reset the line
					   number and the name of the file */
			if (freopen(innames[i], "r", stdin) == NULL) {
					/* attaches the preopened stdin to the 
					   source file, thereby opening it */
				yyerror( "Can't open source file %s\n",
					innames[i]);
				exit(2);
			}
			/* stdio is NOT used to read the input characters */
			/* we use read directly, into our own buffers */
			yyparse();	/* in asparse.c - go scan and parse
					   the source file */
		}
	}

	closetokfile();		/*kick out the last buffered intermediate text*/
}

testlocals()
{
	register	int	i;
	for (i = 0; i <= 9; i++) {
		if (genref[i])
			yyerror("Reference to undefined local label %df", i);
		lgensym[i] = 1;
		genref[i] = 0;
	}
	lgensym[10]=1; genref[10]=0;    /*SFZ*/

	if (litptr != litpool)
		i = litwrapup();
}

pass1_5()
{
	sortsymtab();
#ifdef DEBUG
	if (debug) dumpsymtab();
#endif
	jxxxfix();
#ifdef DEBUG
	if (debug) dumpsymtab();
#endif
}

open_a_out()
{
	struct stat stb;
	/*
	 *	Open up the a.out file now, and get set to build
	 *	up offsets into it for all of the various text,data
	 *	text relocation and data relocation segments.
	 */
	a_out_file = fopen(outfile, "w");
	if (a_out_file == NULL) {
		yyerror("Cannot create %s", outfile);
		delexit();
	}
	biofd = a_out_file->_file;
	fstat(biofd, &stb);
	biobufsize = stb.st_blksize;
	a_out_off = 0;
}

roundsegments()
{
	register	int	locindex;
	register	long	v;
	/*
	 *	round and assign text segment origins
	 *	the exec header always goes in usefile[0]
	 */
	tsize = 0;
	for (locindex=0; locindex<NLOC; locindex++) {
		v = round(usedot[locindex].e_xvalue, ((1<<maxalign)-1));
		usedot[locindex].e_xvalue = tsize;
		if ((locindex == 0) || (v != 0) ){
			usefile[locindex] = (BFILE *)Calloc(1, sizeof(BFILE));
			bopen(usefile[locindex], a_out_off);
			if (locindex == 0)
				a_out_off = sizeof (struct exec);
		} else {
			usefile[locindex] = (BFILE *)-1;
		}
		tsize += v;
		a_out_off += v;
	}
	/*
	 *		Round and assign data segment origins.
	 */
	datbase = round(tsize, ((1<<maxalign)-1));
	for (locindex=0; locindex<NLOC; locindex++) {
		v = round(usedot[NLOC+locindex].e_xvalue, ((1<<maxalign)-1));
		usedot[NLOC+locindex].e_xvalue = datbase + dsize;
		if (v != 0){
			usefile[NLOC + locindex] = (BFILE *)Calloc(1,sizeof(BFILE));
			bopen(usefile[NLOC + locindex], a_out_off);
		} else {
			usefile[NLOC + locindex] = (BFILE *)-1;
		}
		dsize += v;
		a_out_off += v;
	}
	/*
	 *	Assign final values to symbols
	 */
	hdr.a_bss = dsize;
	freezesymtab();		/* this touches hdr.a_bss */
	stabfix();
	/*
	 *	Set up the relocation information "files" to
	 *	be zero; outrel takes care of the rest
	 */
	for (locindex = 0; locindex < NLOC + NLOC; locindex++){
		rusefile[locindex] = (struct relbufdesc *)0;
	}
}

build_hdr()
{
	/*
	 *	Except for the text and data relocation sizes,
	 *	calculate the final values for the header
	 *	
	 *	Write out the initial copy; we to come 
	 *	back later and patch up a_trsize and a_drsize,
	 *	and overwrite this first version of the header.
	 */
	hdr.a_magic = MAGIC;
	hdr.a_text = tsize;
	hdr.a_data = dsize;
	hdr.a_bss -= dsize;
	hdr.a_syms = sizesymtab();	/* Does not include string pool length */
	hdr.a_entry = 0;
	hdr.a_trsize = 0;
	hdr.a_drsize = 0;

	bwrite((char *)&hdr, sizeof(hdr), usefile[0]);
}

i_pass2()
{
	if (useVM == 0) {
		fclose(tokfile);
		tokfile = fopen(tokfilename, "r");
		if (tokfile==NULL) {
		   yyerror("Bad pass 2 temporary file for reading %s", tokfilename);
		   delexit();
		}
	}
	fclose(strfile);
	strfile = fopen(strfilename, "r");
}

pass2()
{
#ifdef DEBUG
	if (debug)
		printf("\n\n\n\t\tPASS 2\n\n\n\n");
#endif DEBUG
	passno = 2;
	lineno = 1;
	dotp = &usedot[0];
	txtfil = usefile[0];	/* already opened (always!) */
	relfil = 0;		/* outrel takes care of the rest */
	initoutrel();

	inittokfile();

	yyparse();

	if (litptr != litpool)
		(void) litwrapup();

	closetokfile();
}

fillsegments()
{
	int	locindex;
	/*
	 *	Round text and data segments to FW by appending zeros
	 */
	for (locindex = 0; locindex < NLOC + NLOC; locindex++) {
		if (usefile[locindex]) {
			txtfil = usefile[locindex];
			dotp = &usedot[locindex];
			while (usedot[locindex].e_xvalue & FW)
				outb(0);
		}
	}
}

reloc_syms()
{
	u_long	closerelfil();
	/*
	 *	Move the relocation information to a.out
	 *	a_out_off is the offset so far:
	 *	exec + text segments + data segments
	 */
	relocfile = (BFILE *)Calloc(1,sizeof(BFILE));
	bopen(relocfile, a_out_off);
	a_out_off += closeoutrel(relocfile);

	hdr.a_trsize = trsize;
	hdr.a_drsize = drsize;
	if (readonlydata) {
		hdr.a_text += hdr.a_data;
		hdr.a_data = 0;
		hdr.a_trsize += hdr.a_drsize;
		hdr.a_drsize = 0;
	}
	/*
	 *	Output the symbol table and the string pool
	 *
	 *	We must first rewind the string pool file to its beginning,
	 *	in case it was seek'ed into for fetching ascii and asciz
	 *	strings.
	 */
	fseek(strfile, 0, 0);
	symwrite(relocfile);
}

fix_a_out()
{
	if (lseek(a_out_file->_file, 0L, 0) < 0L)
		yyerror("Reposition for header rewrite fails");
	if (write(a_out_file->_file, (char *)&hdr, sizeof (struct exec)) < 0)
		yyerror("Rewrite of header fails");
}

delexit()
{
	int	rc;
	delete();
	if (passno == 2){
		unlink(outfile);
	}
	rc = (anyerrs != 0);
	if (d4error) rc |= 4;
	if (dseterr && d124&BROPT) rc |= 2;
	exit(rc);
}

delete()
{
	if (useVM == 0 || tokfilename[0])
		unlink(tokfilename);
	if (strfilename[0])
		unlink(strfilename);
}

sawabort()
{
	char	*fillinbuffer();
	while (fillinbuffer() != (char *)0)
		continue;
	delete();
	exit(1);	/*although the previous pass will also exit non zero*/
}

panic(fmt, a1, a2, a3, a4)
	char	*fmt;
	/*VARARGS 1*/
{
	yyerror("Assembler panic: bad internal data structure.");
	yyerror(fmt, a1, a2, a3, a4);
	delete();
	abort();
}
