/*
	Copyright 1981
	Alcyon Corporation
	8716 Production Ave.
	San Diego, Ca.  92121
*/

/* pass 2 miscellaneous routines */

#include "as68.h"
int p2gi();

long stlen;
int stdofd 1;
char tfilname[];
int ins[], rlbits[], f2mode[];
int udfct, ftudp, pline, prsp;

clrea(ap)
struct op *ap;
{
	register struct op *p;

	p = ap;
	p->ea = p->len = p->xmod = p->drlc = 0; p->con = 0;
	p->ext = p->idx = -1;
}

/*
 * get one operand effective adddress (operand until , or EOS)
 * returns:
 *	opnd[opn].ea set to effective address mode bits
 *	opnd[opn].len set to # bytes for operand
 *	opnd[opn].con set to constant part of ea
 *	opnd[opn].ext set to external symbol # if any
 *	opnd[opn].idx set to index register if any
 *	opnd[opn].drlc set to effective address relocation mode
 *	opnd[opn].xmod set to index register addressing mode (word or long)
 */
getea(opn)
{
	register i,disp;
	register struct op *p;
	register int t;

	p = &opnd[opn];
	disp = 0;
	clrea(p);
	if(ckitc(pitw,'#')) {
		p->len = (modelen==1) ? 2 : modelen;
		p->ea = IMM;
		pitw++;
		goto dosimp;
	}
	if(ckitc(pitw,'(')) {
geteal1:
		pitw++;
		if((i=getrgs()) == PC) {	/*pc relative*/
			p->ea = 072;			/*set mode & register bits*/
			p->len = 2;
		}
		else {
			if(i != -1)		/*last was some type of register*/
				pitw--;		/*havent used it yet*/
			if((i=getareg()) < 0) {	/*not a reg # next*/
				if(disp || getreg()!=-1) {
					uerr(14);	/*illegal index reg*/
					return;
				}
				pitw--;
				goto dosimp;		/*must be expression in ()*/
			}
			p->ea = i&7;				/*put in a reg #*/
		}
		if(ckitc(pitw,',')) {	/*must be index reg #*/
			do_ireg(p,i);
			return;
		}
		ckrparen();
		if(i != PC) {
			if(!disp && ckitc(pitw,'+')) {
				pitw++;
				p->ea =| INDINC;
			}
			else if(disp) {		/*indirect with displacement*/
				p->ea =| INDDISP;
				p->len = 2;
			}
			else 
				p->ea =| INDIRECT;
		}
		ckeop(9+opn);
		return;
	}
	if(ckitc(pitw,'-')) {	/*predecrement maybe*/
		pitw++;
		if(ckitc(pitw,'(')) {	/*must be*/
			pitw++;
			if((i = getareg()) < 0) {	/*not valid a reg*/
				pitw =- 2;		/*must be negative expr*/
				goto dosimp;
			}
			p->ea = i|DECIND;
			ckrparen();
			ckeop(9+opn);
			return;
		}
		pitw--;
	}
dosimp:					/*simple addr or imm expr*/
	if(i=gspreg()) {
		t = ins[0];
		if(i==PC || (i==USP && t!=MOVE))
			uerr(20);
		if(i==SR || i==CCR) {
			if(t!=AND&&t!=OR&&t!=EOR&&t!=ANDI&&t!=ORI&&t!=EORI&&t!=MOVE)
					uerr(20);
		}
		p->idx = i;
		ckeop(9+opn);
		return;
	}
	if((i=getreg()) >= 0) {	/*register direct*/
		p->ea = i;
		if(modelen==1 && i>=AREGLO && i<=AREGHI)
			uerr(20);
		ckeop(9+opn);
		return;
	}
	expr(&p2gi);
	if(pitw < pnite)	/*expr passes one token*/
		pitw--;
	if(extflg) {
		p->ext = extref;
		extflg = 0;
	}
	p->con = ival;
	p->drlc = reloc;		/*relocation factor*/
	if(ckitc(pitw,'(')) {
		disp++;
		goto geteal1;
	}
	if(!p->ea) {	/*memory  address*/
		if(shortadr && (!ival.wd1 || ival.wd1== -1)) { /*16-bit addrs*/
			p->ea = SADDR;
			p->len = 2;
		}
		else {
			p->ea = LADDR;
			p->len = 4;
		}
	}
	ckeop(9+opn);
}

do_ireg(p,i,opn)
struct op *p;
int i, opn;
{
	pitw++;
	p->idx = getreg();
	if(p->idx<0 || p->idx>AREGHI)
		uerr(14);
	p->len = 2;
	if(!ckitc(pitw,')')) {
		p->xmod = getrgs() - 20;
		if(p->xmod<0 || p->xmod>1) {
			uerr(34);
			p->xmod = 0;
		}
	}
	ckrparen();
	ckeop(9+opn);
	if(i==PC)
		p->ea =+ 1;
	else
		p->ea =| INDINX;
}

/*
 * get an A register specification
 *  call with:
 *		pitw pointing to reg operand
 *  returns:
 *		-1 if not vaid A reg
 *		A reg # if valid
 *		also updates pitw if valid
 */
getareg()
{
	register i;

	i = getreg();
	if(i>=AREGLO && i<=AREGHI) {
		return(i&7);
	}
	else {
		if(i != -1)
			pitw--;
		return(-1);
	}
}

/*
 * get any register specification
 *  call with :
 *		pitw pointing at operand
 *  returns:
 *		register # with pitw updated
 *		-1 if not valid register
 */
getreg()
{
	register i;

	i = getrgs();
	if(i>=0 && i<=AREGHI)
		return(i);
	else {
		if(i != -1)
			pitw--;
		return(-1);
	}
}

/*get any register specification*/
getrgs()
{
	register char *i;

	if(pitw->itty == ITSY) {
		i = pitw->itop.ptrw2;		/*symbol ptr*/
		if(i->flags&SYER) {
			pitw++;
			return(i->vl1.wd2);		/*register #*/
		}
	}
	return(-1);
}

/* check for a right paren as the next char*/
/*  output error msg if not found*/
ckrparen()
{
	if(ckitc(pitw,')'))	/*found it*/
		pitw++;
	else
		uerr(32);
}

/*
 * check intermedate text item for special character
 *	call with:
 *		pointer to desired item in stbuf
 *		character to check for
 *	returns:
 *		0 => no match
 *		1 => match
 */
ckitc(ckpt,cksc)
char *ckpt;
{
	if(ckpt >= pnite || ckpt->itty != ITSP || ckpt->itop.wd2 != cksc)
		return(0);
	return(1);
}

/*
 * read intermediate text for one statement
 * returns:
 *	intermediate text in stbuf
 */
ristb()
{
	register riix;
	register int *pi;
	register int i;

	do {
		riix = stbuf[0].itrl;
		itoffset =+ (riix&0377) * STBFSIZE;
		pi = &stbuf[0];
		for(i=0; i<(STBFSIZE)/(sizeof *pi); i++) {
			if(pitix > &itbuf[ITBSZ-1]) {	/*need new buffer of it*/
				doitrd();		/*read it buffer*/
			}
			*pi++ = *pitix++;		/*first word of it*/
		}
		if(stbuf[0].itty != ITBS)	/*best be beginning of statement */
			abort();

/* get the rest of the statement it*/
		riix = stbuf[0].itrl & 0377;	/*unsigned byte*/
		riix--;					/*already got first entry*/
		while(riix--) {
			for(i=0; i<(STBFSIZE)/(sizeof *pi); i++) {
				if(pitix > &itbuf[ITBSZ-1]) {	/*need new buffer of it*/
					doitrd();
				}
				*pi++ = *pitix++;
			}
		}
	} while(stbuf[1].itrl == -1);	/* eliminated instr, read next one */
}

int errno;

doitrd()
{
	register int i;

	pitix = itbuf;
	if((i=read(itfn,itbuf,ITBSZ*(sizeof i)))<=0) {
		putchar(0);
		stdofd = 2;
		printf("it read error i=%d errno=%o\n",
			i,errno);
		putchar(0);
		abort();
	}
}

/*
 * check for end of operand
 * call with
 *		error number if this is not end of operand
 */
ckeop(uen)
{
	if(pitw>=pnite)	/*end of all operands*/
		return(1);
	if(!ckitc(pitw,',')) {		/*not end of stmt must be op,op*/
		uerr(uen);
		return(0);
	}
	return(1);
}

/* output symbol table to file*/
osymt()
{
	register char **sx1;
	register char *p;
	register i;

	stlen = 0;
	if(extindx) {				/*output external symbols first*/
		if(prtflg)
			printf("\n\n-EXTERNAL SYMBOLS-\n");
		sx1 = extbl;
		for(i=0;i<extindx;i++)	/*go through external table*/
			osyme(*sx1++);			/*output symbol*/
	}
	if(prtflg)
		printf("\n\n");

	for(p=bmte; p<lmte; p=+ STESIZE) {	/*want them in order defined*/
		if(p->flags&(SYXR|SYIN))
			continue;
		osyme(p);
	}
}

/* make all undefined symbols external*/
fixunds()
{
	register char **sx1, **sx2;

/* loop thru symbol initial reference table*/
	for(sx1= sirt; sx1<&sirt[SZIRT-1]; sx1 =+ 2) {
		if(*(sx2 = sx1+1)==0)		/* this chain is empty*/
			continue;

/* symbols on one chain*/
		sx2 = *sx2;	/*first entry on this chain*/
		while(1) {
			if(!(sx2->flags&SYDF)) {	/*not defined*/
				if(undflg || sx2->flags&SYGL) { /*all or globals*/
					sx2->flags = sx2->flags|SYDF|SYXR;
					mkextidx(sx2);
				}
			}
			if(sx2 == *sx1)	/*end of chain*/
				break;
			sx2 = sx2->tlnk;	/*next entry in chain*/
		}
	}
}

/*
 * output symbols in a form to be read by a debugger
 * call with pointer to symbol table entry
 * prints all undefined symbols
 */
osyme(aosypt)
struct symtab *aosypt;
{
	register struct symtab *osypt;
	register char *p1;
	register int i;
#ifdef VAX11
	register short *ps1;
#else
	register int *ps1;
#endif

	osypt = aosypt;		/*pointer to symbol table entry*/
	if(!prtflg && !(osypt->flags&SYDF)) {	/*undefined symbol*/
		pudfs(osypt);				/*print undefined*/
		return;
	}

	p1 = &(osypt->name[0]);
	if(prtflg && !((osypt->flags)&SYER)) {	/*put symbol on listing*/
		if(osypt->flags&SYXR)	/*external symbol*/
			printf("%s\n",p1);		/*just print name*/
		else if (osypt->flags&SYDF) {
			printf("%s\t",p1);
			puthex(osypt->vl1.wd1,4);
			puthex(osypt->vl1.wd2,4);
			if(osypt->flags&SYRA)	/*print relocation factor*/
				printf("\tDATA\n");
			else if(osypt->flags&SYRO)
				printf("\tTEXT\n");
			else if(osypt->flags&SYBS)
				printf("\tBSS\n");
			else printf("\tABS\n");
		}
		else {	/*undefined*/
			nerror++;
			printf("%s\t*UNDEFINED*\n",p1);
		}
	}

	stlen =+ 14;	/*one more symbol out*/

/*output symbol to loader file*/
	ps1 = &(osypt->name[0]);
	for(i=0; i<NAMELEN/2; i++) {		/*output symbol name*/
		putw(*ps1++,&lbuf);
	}

	putw(osypt->flags,&lbuf);		/* output symbol flags */
	if(osypt->flags&SYXR) {		/*external symbol*/
		putw(0,&lbuf);
		putw(osypt->vl1.wd1,&lbuf);
	}
	else {
		putw(osypt->vl1.wd1,&lbuf);	/*upper half symbol value*/
		putw(osypt->vl1.wd2,&lbuf);		/*lower symbol value*/
	}
}

/*
 * print undefined symbols
 * call with
 *	pointer to undefined symbol
 */
pudfs(udspt)
struct symtab *udspt;
{
	nerror++;
	if(!ftudp) {		/*first time thru*/
		printf("\n&& UNDEFINED SYMBOLS &&\n");
		ftudp++;
		udfct=0;		/*no symbols on this line*/
	}

	printf("%8s  ",&(udspt->name[0]));
	if(udfct++ > 6) {
		printf("\n");
		udfct=0;
	}
}

/*
 * output source and object listing
 *	call with
 *		2 => print address and binary code only
 *		1 => object in ins[] and instr type in format
 *		0 => print address only
 */
print(pflag)
{
	register i,j;
	register int *pi;

	if( !prtflg ) return;		/*no printing desired*/
	if(fchr==EOF)	return;		/*end of source file*/

	i = instrlen; instrlen = 1;		/*to print preceeding lines*/
	while(pline<p2absln) {		/*need to print some lines*/
		printf("%4d ",pline);			/*put source line num on listing*/
		printf("                        ");			/*align the source*/
		prtline(1);
		putchar('\n');
		fchr=gchr();
		if(fchr==EOF) return;
		pline++;
	}
	instrlen = i;

/* output current address, binary, and source*/
	printf("%4d ",p2absln);			/*put source line num on listing*/
	puthex(loctr.wd1,4);
	puthex(loctr.wd2,4);
	putchar(' ');
	if(!pflag) {			/*no binary*/
		printf("                   ");	/*blanks instead*/
	}
	else {
		pi = ins;
		for(i=0; i<instrlen/2; i++) {	/* binary*/
			puthex(*pi++,4);
		}
		if(instrlen&1)
			puthex(*pi,2);
		putchar(' ');
		for(;i<4;i++) {				/*four bytes max per line*/
			printf("    ");		/*align the source*/
		}
	}
	if(pline>p2absln || pflag==2) {
		putchar('\n');			/*end of line*/
	}
	else {
		prtline(0);
		if(fchr==EOF) return;
		putchar('\n');
		fchr=gchr();
		pline++;
	}
}

/*print one line aligning source output*/
prtline(flg)
{
	register i;
	register col, blcnt;

	if(fchr=='*' || flg) {	/*comment*/
		while(fchr!=EOLC && fchr!=EOF) {
			putchar(fchr);
			fchr = gchr();
		}
		return;
	}
	col = 1;
	blcnt = 0;
	while(1) {
		if(fchr==EOLC || fchr==EOF)
			return;
		if(fchr==' '&& blcnt<3) {
			i= (++blcnt == 3) ? 017 : 7;
			while(col&i) {
				putchar(' ');
				col++;
			}
			while(fchr==' ')
				fchr=gchr();
			if(fchr==EOLC || fchr==EOF)
				return;
		}
		putchar(fchr);
		fchr=gchr();
		col++;
	}
}

/* buffered putchar routine*/
putchar(c)
char c;
{
	register int i;

	*prtchidx++ = c;
	if(!c || prtchidx>=&prtchars[PRTCHLEN]) {
		i = prtchidx - prtchars;
		write(stdofd,prtchars,i);
		prtchidx = prtchars;
	}
}

int hibytflg[4], hibytw[4];

outbyte(bv,br)
{
	if (rlflg == BSS) {		/* [vlh] */
		uerr(39);
		return;
	}
	if(hibytflg[rlflg]) {
		outword(hibytw[rlflg]|(bv&0xff),br);
		hibytflg[rlflg] = 0;
	}
	else {
		hibytw[rlflg] = bv<<8;
		hibytflg[rlflg]++;
	}
}

outword(val,rb)
{
	switch(rlflg) {

		case TEXT:
			putw(val,&lbuf);
			putw(rb,&tbuf);
			break;

		case DATA:
			putw(val,&dabuf);
			putw(rb,&drbuf);
			break;

		case BSS:
			uerr(39);
			break;

		default:
			rpterr("& outword: bad rlflg\n");
			abort();
	}
}

outinstr()
{
	register i;
	register int *p1, *p2;

	i = instrlen>>1;
	p1 = ins;
	p2 = rlbits;
	while(i--) {
		outword(*p1++, *p2++);
	}
}

/* copy data bits from temporary file to loader file*/
cpdata()
{
	myfflush(&lbuf);
	myfflush(&dabuf);
	docp(dafn,dafnc);
}

/* copy text then data relocation bits from temporary file to loader file*/
cprlbits()
{
	myfflush(&lbuf);
	myfflush(&drbuf);
	docp(trbfn, trbfnc);
	docp(drbfn, drbfnc);
}

/*
 * copy one of the temporary files to the loader file
 * call with:
 *	file descriptor of the temporary file
 *	last char of the temporary file name
 */
docp(cfn,cfnc)
{
	register i;

	close(cfn);
	LASTCHTFN = cfnc;
	cfn = openfi(tfilname,0);
	while((i=read(cfn,itbuf,512)) > 0) {
		if(write(lfn,itbuf,i) != i) {
			rpterr("& file write error\n");
			abort();
		}
	}
}

/* print one word in hex*/
puthex(v,l)
{
	register i,j,k;

	j = 12;
	for(i=0; i<l; i++) {
		k = (v>>j)&017;
		k =+ (k >= 10) ? ('A'-10) : '0';
		putchar(k);
		j =- 4;
	}
}

/* check for a control operand*/
controlea(ap)
struct op *ap;
{
	register i;

	i = ap->ea&070;
	if(i==INDIRECT || i==INDDISP || i==INDINX)
		return(1);
	if(i==070) {
		if((ap->ea&7) <= 3)
			return(1);
	}
	return(0);
}

ckcomma()
{
	if(ckitc(pitw,',')) {		/*next token a comma*/
		pitw++;
		return(1);
	}
	return(0);
}

/*
 * generate any necessary additional words for the effective address
 *  call with:
 *		pins pointing to next available word in ins[]
 *		prlb pointing to next available word in rlbits[]
 *		argument is ptr to op structure
 *
 * returns:
 *		appropriate words in ins[] and rlbits[] for operand
 *		pins and prlb updated.
 */
doea(apea)
struct op *apea;
{
	register i,j;
	register struct op *p;

	p = apea;
	switch((p->ea>>3)&7) {		/* ea mode bits*/

	default:		/*no more words*/
		return;

	case 5:			/* d(An)*/
		dodisp(p);
		return;

	case 6:			/* d(An,Ri)*/
dindx:
		if (p->con > 127L || p->con < -128L) {
			uerr(35);
		}
		i = (p->con.wd2&0377) | (p->idx<<12) | (p->xmod<<11);
		if(p->drlc != ABS)
			uerr(27);
		*pins++ = i;
		*prlb++ = DABS;
		instrlen =+ 2;
		return;

	case 7:		/*xxx.W, xxx.L, or #xxx*/
		switch(p->ea&7) {

		case 1:		/* xxx.L*/
			doupper(p);
			p->con.wd1 = 0;		/*clear for dodisp check*/

		case 0:		/* xxx.W*/
			dodisp(p);
			return;

		case 2:		/*d(PC)*/
		case 3:		/*d(PC,Ri.X)*/
			if(p->drlc != ABS) {
				if(p->drlc != rlflg)	/*not same reloc base*/
					uerr(27);
				p->con =- (loctr+instrlen);
				p->drlc = ABS;
			}
			if((p->ea&7) == 3)		/*d(PC,Ri.X)*/
				goto dindx;
			dodisp(p);
			return;

		case 4:		/* #xxx*/
			chkimm(p);		/*check for valid length*/
			if(modelen == 4) {		/*instr mode is long*/
				doupper(p);
				p->con.wd1 = 0;		/*clear for dodisp check*/
			}
			dodisp(p);
			return;
		}
	}
}

dodisp(ap)
struct op *ap;
{
	register struct op *p;

	p = ap;
	*pins++ = p->con.wd2;		/*displacement*/
	if(p->con.wd1 && p->con.wd1 != -1)
		uerr(41);		/*invalid 16-bit disp*/
	*prlb++ = (p->ext != -1) ? (p->ext<<3)|EXTVAR : p->drlc;
	instrlen =+ 2;
}

doupper(p)
struct op *p;
{
	*pins++ = p->con.wd1;	/*upper half of long addr or constant*/
	*prlb++ = LUPPER;
	instrlen =+ 2;
}

/*
 * build a format 1 (add, sub, and, etc) instr
 * call with:
 *	register #
 *	mode bits
 *	ptr to operand structure for effective address
 */
makef1(arreg, armode, apea)
struct op *apea;
{
	register i,j;
	register struct op *p;

	p = apea;
	ins[0] =| (arreg<<9);	/*put in reg #*/
	ins[0] =| armode;	/*instr mode bits*/
	ins[0] =| p->ea;		/*put in effective addr bits*/
	doea(p);			/*may be more words in ea*/
}

/* generate an immediate instr*/
genimm()
{
	ins[0] =| (f2mode[modelen] | opnd[1].ea);
	if(modelen == 4) {
		doupper(&opnd[0]);
		opnd[0].con.wd1 = 0;	/*clear for dodisp check*/
	}
	chkimm(&opnd[0]);	/*check for valid immed length*/
	dodisp(&opnd[0]);
	doea(&opnd[1]);
}

chkimm(ap)
struct op *ap;
{
	register struct op *p;

	p=ap;
	if(modelen == 2) {	/*word*/
		if(p->con.wd1 && p->con.wd1!=-1)
			uerr(42);
	}
	else if(modelen == 1) {	/*byte*/
		if(p->con.wd1 && p->con.wd1!=-1)
			uerr(43);
		if(p->con.wd2>255 || p->con.wd2<=-256)
			uerr(43);
	}
}

/* try to make a normal instr into an immediate instr*/
makeimm()
{
	if(opnd[0].ea != IMM)
		return(0);
	if(!dataalt(&opnd[1]))
		return(0);
	if(opcpt == addptr)
		opcpt = addiptr;
	else if(opcpt == andptr)
		opcpt = andiptr;
	else if(opcpt == orptr)
		opcpt = oriptr;
	else if(opcpt == subptr)
		opcpt = subiptr;
	else if(opcpt == cmpptr)
		opcpt = cmpiptr;
	else if(opcpt == eorptr)
		opcpt = eoriptr;
	else
		return(0);
	ins[0] = opcpt->vl1.wd2;
	format = (opcpt->flags)&OPFF;
	genimm();
	return(1);
}

ckbytea()
{
	if(modelen==1 && !dataea(&opnd[0]))
		uerr(20);	/*byte mod not allowed*/
}

/* get a special register token (CCR, SR, or USP)*/
gspreg()
{
	register i;

	i = getrgs();
	if(i>AREGHI)
		return(i);
	if(i != -1)
		pitw--;
	return(0);
}

/*
 * check an operand for a special register
 * call with:
 *  ptr to operand struct
 *  special register value
 */
cksprg(ap,v1)
struct op *ap;
{
	if(ap->ea)
		return(0);
	if(ap->idx == v1)
		return(1);
	return(0);
}

/* check for operand as any special register*/
anysprg(ap)
struct op *ap;
{
	if(ap->ea)
		return(0);
	if(ap->idx>=CCR && ap->idx<=USP)
		return(1);
	return(0);
}

/* copy opnd 0 to opnd 1*/
cpop01()
{
	opnd[1].ea = opnd[0].ea;
	opnd[1].len = opnd[0].len;
	opnd[1].con = opnd[0].con;
	opnd[1].drlc = opnd[0].drlc;
	opnd[1].ext = opnd[0].ext;
	opnd[1].idx = opnd[0].idx;
	opnd[1].xmod = opnd[0].xmod;
}

cksize(ap)		/* [vlh] try to check displacement range */
struct op *ap;
{
	long value;

	if ((ap->ea&070) != 070) return;
	value = (ap->con>0 && ap->con&0100000) ? -(ap->con&~0100000) : ap->con;
	if (modelen == 1) {
		if (value < -128L || value > 127L)	/* 8 bits */
			uerr(35);
	}
	else if (modelen == 2)
		if (value > 32767L || value < -32768L)	/* 16 bits */
			uerr(41);
}

ccr_or_sr()		/* [vlh] */
{
	if(opnd[1].idx==CCR)
		modelen = 1;		/*byte mode only*/
	else	/* [vlh] SR */
		if (modelen != 2) {
			modelen = 2;
			uerr(34);
		}
	cksize(&opnd[0]);
	ins[0] =| IMM | f2mode[modelen];
	dodisp(&opnd[0]);
}

get2ops()
{
	getea(0);		/*get first effective address*/
	if(!ckcomma()) {
		uerr(10);
		return(1);		/*no second op*/
	}
	getea(1);		/*get second effective address*/
	return(0);
}
