

        AS                                                            AS


        PROGRAM 
             as - assembler for the Motorola 68000 

        USAGE 
             as [-i] [-p] [-u] [-L] [-N] sourcefile 

        FUNCTION 
             'as' is the REGULUS 68000 assembler. It is used to assemble 
             a program for the Motorola 68000. The  syntax  accepted  is
             identical   to   the  Motorola  68000  cross  assembler  as
             described  in  Motorola  Manual   M68KXASM(D3)   with   the
             exceptions  and  additions described below. The source file
             must be present as the first argument. The assembler always 
             produces a relocatable object file whose name is  the  same
             as  the  source  file primary name with a ".o" extension --
             ie. if the source file name is  "test.s"  then  the  object
             file  name  is  "test.o"; if the source file name is "pgm1"
             then the object file name is "pgm1.o". 

             The -i option is used to initialize the  assembler  in  the
             case  that  the  file  '/lib/as68symb'  does  not  exist or
             becomes  corrupted.  It  also  requires  the  use  of   the
             initialization  file /lib/as68init. The command would be of
             the form : 'as -i /lib/as68init'. 

             If the -p flag  is  specified,  the  assembler  produces  a
             hexidecimal  side-by-side  listing  on  the standard output
             (you may want to redirect this to a file).  Error  messages
             are  also  produced  on the standard output file whether or
             not the -p flag is specified. 

             If the -u flag is specified, all undefined symbols  in  the
             assembly are treated as global. 

             If  the  -L  flag  is  specified, all address constants are
             generated as 32-bit  numbers.  Default  is  16-bit  numbers
             where possible. 

             If  the  -N flag is specified, pass 1.5 of the assembler is
             not executed. This pass changes all long relative  branches
             to short relative branches where possible. 

             Error messages begin with an &, indicate the source line on 
             which   the   error   occurred   and   are   meant   to  be
             self-explanatory. The error messages preceded by  a  single
             '&'  are  generated  during the first pass of the assembler
             and those preceded by a pair '&&' are generated during  the
             second pass of the assembler. 

             The  following  are  differences between this assembler and
             the Motorola Cross Assembler: 

                  The  assembler  accepts  both  upper  and  lower  case
                  characters.  Labels  and  variables are case sensitive
                  ('LOOP' is different from 'Loop'), but  mnemonics  and


        REGULUS Reference Manual                                 COMMAND



        AS                                                            AS


                  directives can be in either upper or lower case. 

                  Macros  are implemented using the C preprocessor which
                  is part of the c68 command (see  C68(I)).  Conditional
                  assembly  is  also implemented using the preprocessor.
                  Use the command "c68 -P pgm.s" to expand macros  using
                  the  C  preprocessor.  The  expanded  file  is left on
                  pgm.i. 

                  All  assembler  directives  begin  with  an   optional
                  character    '.'.   The   following   directives   are
                  implemented exactly  as  they  are  described  in  the
                  M68KXASM(D3) manual with a dot in front of their name: 
                  .equ, .end, .dc, and .ds. 

                  All  labels  must  terminate  with  a ':', unless they
                  start in column 1. 

                  Registers may be referenced as r0 - r15, R0 - R15,  D0
                  -  D7,  d0 - d7, A0 - A7, or a0-a7. Registers R8 - R15
                  are the same as A0 - A7. 

             The following assembler directives are implemented just  as
             they are described in the UNIX Assembler Reference Manual: 

             .text, .data, .bss 
                  These  three  directives  instruct  the  assembler  to
                  change the assembler base segment to the  text,  data,
                  or  bss  segment respectively. Each assembly starts in
                  the  text  segment.  It   is   illegal   to   assemble
                  instructions or data into the bss segment. Symbols may 
                  be  defined  and storage may be reserved using the .ds
                  directive in the bss segment. 

             .globl name[,name...] 
             xdef name[,name...] 
             xref name[,name...] 
                  These directives make the name(s)  external.  If  they
                  are  defined  in  the current assembly, this statement
                  makes these names available to other routines during a 
                  load by lo68. If these names are not  defined  in  the
                  current   assembly,  they  become  undefined  external
                  references and lo68 will link them to external  values
                  of  the same name in other routines. Specifying the -u
                  flag will force the assembler to  make  all  undefined
                  names external. 

             .comm name,expression 
                  This directive defines a named block common area. When 
                  several  routines  are  linked together with lo68, all
                  block common areas with the same name  are  loaded  at
                  the  same  address.  The  size  of  this  block common
                  storage area is the largest value  of  the  expression
                  part  of  all  .comm directives with the same name. No
                  error  message  is  produced  if  the  areas  are   of


        REGULUS Reference Manual     - 2 -                       COMMAND



        AS                                                            AS


                  different sizes. 

             .even 
                  If  the  location counter is odd, it is incremented by
                  one so that the next instruction or  data  field  will
                  begin on an even memory boundary. 

                  The  relocation  counter  may  be  manipulated  with a
                  statement like: 
                       *=expr 
                  Care should be exercised when using this facility. The 
                  expression  may  only  move  the  relocation   counter
                  forward. The unused space is filled with zeroes in the 
                  text  or  data  segments and is simply not assigned in
                  the bss segment. This facility requires the  assembler
                  to  not  allow  comment  lines  which begin with "*=".
                  Comments beginning with "* =" are allowed. 

             ASCII string constants may be  enclosed  in  single  quotes
             (ie. 'ABCD') or in double quotes (ie. "ac14"). 

             The   following   assembler   directives   have  also  been
             implemented: 

             section # 
                  This directive is used to define a base  segment  like
                  the  .bss, .data and .text directives discussed above.
                  The sections  can  be  numbered  0  to  15  inclusive.
                  Section 14 maps to data, section 15 is bss and all the 
                  others are text sections. 

             offset expression 
                  This  directive  creates  a  dummy storage section. No
                  code  generating  instructions  may   occur.   It   is
                  terminated by a 'section', '.data', '.bss', '.text' or 
                  '.end'.   The  offset  table  begins  at  the  address
                  specified in the expression. 

             dcb 
                  Just like the dc.b command 

             reg reglist 
                  This directive builds a register  mask  which  can  be
                  used  by  the movem instruction. One or more registers
                  can be listed  in  increasing  order  of  the  form  :
                  'R?[-R?[/R?[-R?...]...]]'. Where R? can be replaced by 
                  A0-A7,  D0-D7 and R0-R15. The register list might look
                  like : A2-A4/A7/D1/D3-D5. The registers  may  also  be
                  designated separated by commas (eg. A1,A2,D5,D7). 

             Conditional  assembly  directives  can  have  any  level of
             nesting. The  following  conditional  assembler  directives
             have been implemented: 

             ifeq expression 


        REGULUS Reference Manual     - 3 -                       COMMAND



        AS                                                            AS


             ifne expression 
             ifle expression 
             iflt expression 
             ifge expression 
             ifgt expression 
                  The  expression  is  tested  against  zero (with ifeq:
                  equal, ifne: not equal, ifle:  less  or  equal,  iflt:
                  less  than, ifge: greater or equal, ifgt: greater) and
                  if it is evaluated true  then  the  code  enclosed  is
                  assembled,  otherwise  the  code  is ignored until the
                  matching endc is found. 

             ifc 'string1','string2' 
             ifnc 'string1','string2' 
                  The two strings are compared.  The  'c'  condition  is
                  true  if they are exactly the same, the 'nc' condition
                  is true if they do not match. 

             endc 
                  Signifies the end of  the  code  to  be  conditionally
                  assembled. 

        FILES 
             /lib/as68symb 
             /tmp/a6????A (???? is the process id number) 
             /lib/as68init 

        EXTRAS 
             The  following  enhancements  have  been  added  to aid the
             assembly  language  programmer  by  making   the   assembly
             language more regular: 

             move,  add,  sub  mnemonics  will  actually generate moveq,
             addq, and suba  instructions  where  possible.  If  a  move
             instruction  rather  than  a  moveq  instruction is desired
             (affecting only lower byte or word of D register), the size 
             attribute must be explicitly coded ie.  move.b  or  move.w.
             The  assembler  will  change any move or move.l to moveq if
             possible. 

             clr.x An is allowed and will  actually  generate  a  suba.x
             An,An instruction. 

             add,  sub,  cmp  with  an A register source/destination are
             allowed and generate adda, suba, cmpa. 

             add, and, cmp, eor, or,  sub  are  allowed  with  immediate
             first  operands  and  actually  generate  addi, andi, cmpi,
             eori, ori, subi instructions if the second operand  is  not
             register direct. 

             All  branch  instructions  generate short relative branches
             where possible, including forward references. 

             Any shift instruction with no shift count specified assumes 


        REGULUS Reference Manual     - 4 -                       COMMAND



        AS                                                            AS


             a shift count of one; ie. "asl r1" is  equivalent  to  "asl
             #1,r1". 

             jsr  instructions  are  changed  to bsr instructions if the
             resulting bsr is shorter than the jsr. 

             Several  additional  mnemonics  have  been  added  to   the
             condition  code  instructions which map to the standard set
             (bt -> bra, bhs -> bhis, bnz -> bne, bze ->  beq,  dbhs  ->
             dbhi, dblo -> dbcs, dbnz -> dbne, dbze -> dbeq, shs -> scc, 
             slo -> scc, snz -> sne, sze -> seq). 

        BUGS 
             .set is currently implemented as a .equ. 











































        REGULUS Reference Manual     - 5 -                       COMMAND/*
	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,(int)'#')) {
		p->len = (modelen==1) ? 2 : modelen;
		p->ea = IMM;
		pitw++;
		goto dosimp;
	}
	if(ckitc(pitw,(int)'(')) {
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,(int)',')) {	/*must be index reg #*/
			do_ireg(p,i);
			return;
		}
		ckrparen();
		if(i != PC) {
			if(!disp && ckitc(pitw,(int)'+')) {
				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,(int)'-')) {	/*predecrement maybe*/
		pitw++;
		if(ckitc(pitw,(int)'(')) {	/*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,(int)'(')) {
		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((int)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,(int)')')) /*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 short *pi;
	register int i;

	do {
		riix = stbuf[0].itrl;
		pi = &stbuf[0];
		for(i=0; i<(sizeof stbuf[0])/(sizeof *pi); i++) {
			*pi++ = doitrd();
		}
		if(stbuf[0].itty != ITBS) {	/*best be beginning of statement */
		  printf("it sync error itty=%x\n",stbuf[0].itty);
			if( stbuf[0].itty == 0)
				return(0);
			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<(sizeof stbuf[0])/(sizeof *pi); i++) {
				*pi++ = doitrd();
			}
		}
	} while(stbuf[1].itrl == -1);	/* eliminated instr, read next one */
	return(1);
}

int errno;
int nitleft;	/* # of shorts left in itbuf	*/

doitrd()
{
	register short i;

	if(pitix < itbuf || pitix > &itbuf[ITBSZ]) {
		printf("doitrd: buffer botch pitix=%lx itbuf=%lx end=%lx\n",
		pitix,itbuf,&itbuf[ITBSZ]);
		endit();
	}

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

/*
 * 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,(int)',')) {	/*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;
	register j;
	int symcmp();
	stlen = 0;
	if(extindx) {				/*output external symbols first*/
		sx1 = extbl;
		for(i=0;i<extindx;i++)	/*go through external table*/
			osyme(*sx1++);			/*output symbol*/
	}

	for(p=bmte; p<lmte; p=+ STESIZE) {	/*want them in order defined*/
		if(p->flags&(SYXR|SYIN))
			continue;
		osyme(p);
	}
	if(prtflg)
	{
		xline = LPP;
		page();				/* Pop to next Page	*/
		printf("S y m b o l   T a b l e\n\n");
		xline++;			/* Bump Line count	*/
		j = ((lmte-bmte)/STESIZE);	/* # elements		*/
		qsort(bmte,j,STESIZE,symcmp);	/* Sort the symbols 1st */
		j = 0;				/* Now count symbols / line*/
		for(p=bmte; p<lmte; p+=STESIZE)
		{
			if(j > 3)
			{
				printf("\n");
				page();
				j = 0;
			}
			j += psyme(p);
		}		
	}
}
symcmp(a,b)
register char *a;
register char *b;
{
	return(strncmp(a,b,NAMELEN));
}
/* 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;
	register short *ps1;

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


	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*/
		if(xline > (LPP-10)) xline = LPP;
		page();
		printf("\n&& UNDEFINED SYMBOLS &&\n");
		xline++;
		ftudp++;
		udfct=0;		/*no symbols on this line*/
	}

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

psyme(osypt)
register struct symtab *osypt;
{
	register char *p1;

	if(((osypt->flags & SYER) != 0) || (osypt->flags&SYIN))
		return(0);

	p1 = &(osypt->name[0]);
	printf("%-8s  ",p1);
	if(osypt->flags&SYXR)
	{
		printf("******** EXT   ");
		return(1);
	}

	if(osypt->flags&SYDF)
	{

		puthex(osypt->vl1.wd1,4);
		puthex(osypt->vl1.wd2,4);
		if(osypt->flags&SYRA)	/*print relocation factor*/
			printf(" DATA  ");
		else if(osypt->flags&SYRO)
			printf(" TEXT  ");
		else if(osypt->flags&SYBS)
			printf(" BSS   ");
		else    printf(" ABS   ");
	}

	else
	{
			nerror++;
			printf("*UNDEFINED*    ");
	}
	return(1);
}


/*
 * 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*/
		page();
		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*/
	page();
	printf("%4d ",p2absln);			/*put source line num on listing*/
	puthex((int)loctr.wd1,4);
	puthex((int)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<5;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++;
	}
}
#ifdef	BLIVOT			/* This was so horrible ...		*/
/*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++;
	}
}
#else
prtline(flg)	/* What you see in the editor is what you get on the listing*/
{
	while(fchr != EOLC && fchr != EOF)
	{
		putchar(fchr);
		fchr = gchr();
	}
}
#endif
/*
 *	Heading print routine
 */
page()
{
     if((prtflg == 0) || (++xline < LPP)) return;
     printf("\014C P / M   6 8 0 0 0   A s s e m b l e r\t\t%s\t\tPage%4d\n",
		"Revision 02.01",++xpage);
     printf("Source File: %s\n\n",sfname);
     xline = 3;
}

/* 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(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,savelc[DATA]);
}

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

/*
 * 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
 *	length to copy
 */
docp(cfn,cfnc,length)
long	length;
{
	register i;
	register j;
	close(cfn);
	LASTCHTFN = cfnc;
	cfn = openfi(tfilname,0);
/*	while((i=read(cfn,itbuf,512)) > 0) { */
	while(length > 0)
	{
		if(length > 512)
			j = 512;
		else
			j = length;

		if((i=read(cfn,itbuf,j)) != j)
		{
			printf("& Read Error On Intermediate File: %s\n",
				tfilname);
			abort();
		}

		if(write(lfn,itbuf,i) != i) {
			rpterr("& Object file write error\n");
			abort();
		}
		length -= j;
	}
}

/* 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);
}
