[comp.sources.amiga] v89i024: a68k - 68000 assembler v2.42, Part02/04

page@swan.ulowell.edu (Bob Page) (03/08/89)

Submitted-by: jlydiatt@jlami.wimsey.bc.ca (Jeff Lydiatt)
Posting-number: Volume 89, Issue 24
Archive-name: languages/a68k242.2

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	A68kmisc.c
#	History.log
#	A68k2do.txt
#	Makefile
#	Makefile.azt
#	Makefile.dbg
#	Makefile.pdc
#	wb_parse.c
# This archive created: Tue Mar  7 20:40:28 1989
cat << \SHAR_EOF > A68kmisc.c
/*------------------------------------------------------------------*/
/*								    */
/*		      MC68000 Cross Assembler			    */
/*								    */
/*	      Copyright	(c) 1985 by Brian R. Anderson		    */
/*								    */
/*	      Miscellaneous routines - January 6, 1989		    */
/*								    */
/*   This program may be copied	for personal, non-commercial use    */
/*   only, provided that the above copyright notice is included	    */
/*   on	all copies of the source code.	Copying	for any	other use   */
/*   without the consent of the	author is prohibited.		    */
/*								    */
/*------------------------------------------------------------------*/
/*								    */
/*		Originally published (in Modula-2) in		    */
/*	    Dr.	Dobb's Journal, April, May, and June 1986.          */
/*								    */
/*	 AmigaDOS conversion copyright 1989 by Charlie Gibbs.	    */
/*								    */
/*------------------------------------------------------------------*/

#include <stdio.h>
#include "a68kdef.h"
#include "a68kglb.h"

char Sdata[MAXSREC];	/* S-record data */
int  Sindex;		/* Index for Sdata */
int  NumRExt, NumR32, NumR16, NumR8;

static char *errmsg[] =	{
    "--- Unknown error code ---",
    "Alignment error.",
    "No such op-code.",
    "Duplicate Symbol.",
    "Undefined Symbol.",
    "Addressing mode not allowed here.",
    "Error in operand format.",
    "Error in relative branch.",
    "Address mode error.",
    "Operand size error.",
    "END statement is missing.",
    "Value must be absolute.",
    "Relocatability error.",
    "INCLUDE file cannot be opened.",
    "Illegal forward reference.",
    "Not supported in S-format.",
    "This instruction needs a label.",
    "Pass 1 / Pass 2 phase error.",
    "ENDM statement is missing.",
    "ENDC statement is missing.",
    "Unmatched ENDC statement.",
    "Too much DC data.",
    "Too many SECTIONs.",
    "Duplicate macro definition.",
    "More than one label on this line.",
    "End of string is missing.",
    "Short displacement can't be zero.",
    ""};

/* Functions */
extern int  LineParts(), Instructions(), ObjDir();
extern int  GetInstModeSize(), GetMultReg(), CountNest();
extern int  ReadSymTab(), GetArgs(), GetAReg(),	OpenIncl();
extern long GetValue(),	CalcValue();
extern char *AddName(),	*GetField();
extern struct SymTab *NextSym();
extern struct SymTab **HashIt();

long AddrBndW(), AddrBndL();



long AddrBndW (v) register long	v;
/* Advances "v" to the next word boundary */
{
    register int i;

    if (v & 1L)	{
	AppendSdata (0L, 1);
	v++;
    }
    return (v);
}



long AddrBndL (v) register long	v;
/* Advances "v" to the next long-word boundary */
{
    long templong;

    v =	AddrBndW (v);		/* Bump	to a word boundary first */
    if (v & 2L)	{		/* If still not	aligned, */
	templong = NOP;		/*  generate a NOP */
	AppendSdata (templong, 2);
	v += 2;
    }
    return (v);
}



WriteListLine (f) struct fs *f;
/* Writes one line to the Listing file,	including Object Code */
{
    register int i, j, printed;
    long templong;
    char macflag;
    char tempstr[12];
    int	 dummy;

    if (!Pass2)
	return;			/* Pass	2 only */
    if (FwdShort && (ErrLim == 0)) {
	DisplayLine (dummy);
	printf ("A short branch can be used here.\n");
    }
    if (SuppList)
	return;			/* Listing is suppresed	*/
    if (ErrLim == 0)
	if ((Dir == Page) || (Dir == Space) || (Dir == Title)
	|| (Dir	== DoList) || (Dir == NoList) || (ListOff))
	    return;		/* Don't print unless they have errors */

    CheckPage (f, FALSE);	/* Print headings if necessary */

    if (PrntAddr) {
	if ((Dir == Equ) || (Dir == Set))
	    LongPut (f,	ObjSrc,	3);	/* Equated value */
	else
	    LongPut (f,	AddrCnt, 3);	/* Current location */
	if (!KeepTabs)
	    xputs (f, "  ");
    } else
	if (!KeepTabs)
	    xputs (f, "        ");      /* Don't print location */
    if (KeepTabs)
	xputs (f, "\t");                /* Use tabs for spacing */
    printed = 8;			/* We've printed 8 positions */
    LongPut (f,	ObjOp, nO);		/* Generated code */
    printed += nO * 2;
    if (nS != 0) {
	xputs (f, " ");
	LongPut	(f, ObjSrc, nS);
	printed	+= nS *	2 + 1;
    }
    if (nD != 0) {
	xputs (f, " ");
	LongPut	(f, ObjDest, nD);
	printed	+= nD *	2 + 1;
    }
    if ((j = nX) > 0) {				/* String data */
	if ((j * 2 + printed) >	ObjMAX)
	    j =	(ObjMAX	- printed) / 2;
	for (i = 0; i <	j; i++)	{
	    templong = ObjString[i];
	    LongPut (f,	templong, 1);
	}
	printed	+= j * 2;
    }
    while (printed < ObjMAX) {
	if (KeepTabs) {
	    xputs (f, "\t");
	    printed += 8;
	    printed &= ~7;
	} else {
	    xputs (f, " ");
	    printed++;
	}
    }
    if ((InFNum	== 0) || (OuterMac == 0))
	macflag	= ' ';                  /* Open code */
    else if (InFNum > OuterMac)
	macflag	= '+';                  /* Inner macro */
    else if ((InFNum ==	OuterMac) && (Dir != MacCall))
	macflag	= '+';                  /* Outermost macro */
    else
	macflag	= ' ';                  /* We're outside macros */

    sprintf (tempstr, "  %5d%c", LineCount, macflag);
    xputs (f, tempstr);
    xputs (f, Line);
    xputs (f, "\n");

    if (FwdShort && (ErrLim == 0))
	xputs (f, "A short branch can be used here.\n");

    for	(i = 0;	i < ErrLim; i++) {	/* Write error messages. */
	CheckPage (f, FALSE);
	xputs (f, errmsg[ErrCode[i]]);
	printed	= strlen(errmsg[ErrCode[i]]);
	while (printed < ObjMAX	+ 8) {
	    if (KeepTabs) {
		xputs (f, "\t");
		printed	+= 8;
		printed	&= ~7;
	    } else {
		xputs (f, " ");
		printed++;
	    }
	}
	for (j = 0; j <	ErrPos[i]; j++)	{
	    if (Line[j]	== '\t') {
		xputs (f, "\t");
		printed	+= 8;
		printed	&= ~7;
	    } else {
		xputs (f, " ");
		printed++;
	    }
	}
	xputs (f, "^ ");                        /* Error flag */
	if (i == 0) {
	    if (InF->UPtr == 0)
		xputs (f, InF->NPtr);		/* Module name */
	    else
		xputs (f, "(user macro)");      /* In a user macro */
	    sprintf (tempstr, " line %d", InF->Line);
	    xputs (f, tempstr);			/* Line	number */
	}
	xputs (f, "\n");
    }
}



WriteSymTab (f)	struct fs *f;
/* Lists the symbol table in alphabetical order	*/
{
    int	 printhunk, i;
    char *p;
    char tempstr[24];
    long templong;
    register int j, k;
    register struct SymTab **ss1, **ss2, *sym, **sortlim;
    struct Ref *ref;

    if (NumSyms	== 0)
	return;			/* The symbol table is empty - exit */

/* Build a sorted table	of pointers to symbol table entries */

    templong = NumSyms * sizeof	(struct	SymTab *);
    SymSort = (struct SymTab **) malloc	((unsigned) templong);
    if (SymSort	== NULL) {
	fprintf	(stderr, "Not enough memory for symbol table sort!\n");
	return;
    }
    sortlim = SymSort +	NumSyms;
    sym	= SymChunk = SymStart;
    sym++;
    SymChLim = (struct SymTab *) ((char	*) SymChunk + CHUNKSIZE);
    ss1	= SymSort;
    while (sym)	{
	*ss1++ = sym;
	sym = NextSym (sym);	/* Try for another symbol table	entry */
    }
    for	(i = NumSyms / 2; i > 0; i /= 2) {		/* Shell sort */
	for (ss1 = SymSort + i;	ss1 < sortlim; ss1++) {	/*  (copied   */
	    for	(ss2=ss1-i; ss2	>= SymSort; ss2	-= i) {	/*  from K&R) */
		if (strcmp ((*ss2)->Nam, (*(ss2+i))->Nam) <= 0)
		    break;
		sym = *ss2;
		*ss2 = *(ss2+i);
		*(ss2+i) = sym;
	    }
	}
    }

/* The table is	now sorted - print the listing */

    LnCnt = LnMax;			/* Skip	to a new page. */
    for	(i = 0,	ss1 = SymSort; i < NumSyms; i++) {
	sym = *ss1++;
	CheckPage (f, TRUE);

	p = sym->Nam;			/* Pointer to symbol */
	if (sym->Flags & 8)
	    p++;			/* Skip	blank preceding	macro name */
	else if	(sym->Flags & 0x10)
	    p += 6;			/* Skip	hunk sequence number */
	sprintf	(tempstr, "%-11s ", p); /* Symbol or macro name */
	xputs (f, tempstr);
	if (strlen (p) > 11)		/* Long	symbol - go to new line	*/
	    if (KeepTabs)
		xputs (f, "\n\t    ");
	    else
		xputs (f, "\n            ");

	printhunk = FALSE;		/* Assume no hunk no. to print */
	if (sym->Defn == NODEF)
	    xputs (f, "  *** UNDEFINED *** ");
	else if	(sym->Flags & 4)
	    xputs (f, "  -- SET Symbol --  ");
	else if	(sym->Flags & 8) {
	    sprintf (tempstr, " +++ MACRO +++ %5d", sym->Defn);
	    xputs (f, tempstr);
	} else if (sym->Flags &	0x10) {
	    j =	(sym->Hunk & 0x3FFF0000L) >> 16;
	    if (j == HunkCode)
		xputs (f, "  CODE    ");
	    else if (j == HunkData)
		xputs (f, "  DATA    ");
	    else
		xputs (f, "  BSS     ");
	    printhunk =	TRUE;
	} else if (sym->Flags &	0x20) {
	    sprintf (tempstr, "      %c%ld  ",
		(sym->Val & 8L)	? 'A' : 'D', sym->Val & 7L);
	    xputs (f, tempstr);
	    printhunk =	TRUE;
	} else {
	    LongPut (f,	sym->Val, 4);		/* Value */
	    xputs (f, "  ");
	    printhunk =	TRUE;
	}
	if (printhunk) {
	    j =	sym->Hunk & 0x00007FFFL;	/* Hunk	number */
	    if (sym->Flags & 0x60)
		xputs (f, " Reg");              /* Register or list */
	    else if (sym->Flags	& 1)
		xputs (f, " Ext");              /* External */
	    else if (j == ABSHUNK)
		xputs (f, " Abs");              /* Absolute */
	    else {
		sprintf	(tempstr, "%4d", j);    /* Hunk number */
		xputs (f, tempstr);
	    }
	    sprintf (tempstr," %5d",sym->Defn); /* Statement number */
	    xputs (f, tempstr);
	}
	if (XrefList) {
	    xputs (f, "  ");
	    if (sym->Ref1 == NULL)
		xputs (f, " *** UNREFERENCED ***");
	    else {
		ref = sym->Ref1;
		j = k =	0;
		while (1) {
		    if (ref->RefNum[j] == 0)
			break;
		    if (k >= 9)	{
			xputs (f, "\n");        /* New line */
			if (KeepTabs)
			    xputs (f, "\t\t\t\t  ");    /* 34 spaces */
			else
			    for	(k = 0;	k < 34;	k++)
				xputs (f, " ");
			k = 0;
		    }
		    sprintf (tempstr, "%5d", ref->RefNum[j]);
		    xputs (f, tempstr);
		    j++;
		    k++;
		    if (j < MAXREF)
			continue;		/* Get the next	slot */
		    if ((ref = ref->NextRef) ==	0)
			break;			/* End of last entry */
		    j =	0;			/* Start the next entry	*/
		}
	    }
	}
	xputs (f, "\n");
    }
    free (SymSort);		/* Free	the sort work area */
    SymSort = NULL;
}



CheckPage (f, xhdr) struct fs *f; int xhdr;
/* Checks if end of page reached yet --	if so, advances	to next	page. */
{
    register int printed;
    char tempstr[12];

    LnCnt++;
    if (LnCnt >= LnMax)	{
	PgCnt++;
	if (PgCnt > 1)
	    xputs (f, "\f");            /* Skip to new page */
	xputs (f, TTLstring);		/* Title */
	printed	= strlen (TTLstring);
	while (printed < 56)
	    if (KeepTabs) {
		xputs (f, "\t");
		printed	+= 8;
		printed	&= ~7;
	    } else {
		xputs (f, " ");
		printed++;
	    }
	xputs (f, SourceFN);		/* File	name */
	if (KeepTabs)
	    xputs (f, "\t");
	else
	    xputs (f, "        ");
	sprintf(tempstr, "Page %d\n\n", PgCnt); /* Page number */
	xputs (f, tempstr);
	LnCnt =	2;
	if (xhdr) {
	    xputs (f, "Symbol       Value    Hunk  Line");
	    if (XrefList)
		xputs (f, "   References");     /* Cross-reference */
	    xputs (f, "\n\n");
	    LnCnt += 2;
	}
    }
}



StartSrec (f, idntname)	struct fs *f; char *idntname;
/* Writes object header	record */
{
    register long CheckSum, templong;
    register char *s;

    if (SFormat) {
	xputs (f, "S0");
	templong = strlen (idntname) + 3; /* extra for addr. & checksum	*/
	LongPut	(f, templong, 1);
	CheckSum = templong;

	xputs (f, "0000");  /* Address is 4 digits, all zero, for S0 */

	s = idntname;
	while (*s) {
	    templong = toupper (*s++);
	    LongPut (f,	templong, 1);
	    CheckSum +=	templong;
	}
	CheckSum = ~CheckSum;		/* Complement checksum */
	LongPut	(f, CheckSum, 1);
	xputs (f, "\n");
    } else {
	templong = HunkUnit;
	xputl (f, templong);
	DumpName (f, idntname, 0L);
    }
    StartAddr =	TempAddr = Sindex = 0;
    NumRExt = NumR32 = NumR16 =	NumR8 =	0;
}



WriteSrecLine (f) struct fs *f;
/* Transfers object code components to output buffer. */
/* Moves long words or portions	thereof. */
{
    register int i;
    register long templong;

    if (HunkType == HunkBSS)
	return;				/* No code in BSS hunk */

    if (nO + nS	+ nD + nX) {		/* If we have object code */
	AppendSdata (ObjOp, nO);	/* Opcode */
	AppendSdata (ObjSrc, nS);	/* Source */
	AppendSdata (ObjDest, nD);	/* Destination */
	for (i = 0; i <	nX; i++) {	/* String data */
	    templong = ObjString[i];
	    AppendSdata	(templong, 1);
	}
    }
}



AppendSdata (Data, n) register long Data; int n;
/* If we are producing S-format	records:
     Transfers "n" low-order bytes from "Data" to the output buffer.
     If	the buffer becomes full, DumpSdata will	be called to flush it.
     S-records will also be broken on 16-byte boundaries.
   If we are producing AmigaDOS	format,	data will be written
     directly to Srec -	we'll go back and fill in the hunk length
     at	the end	of the hunk.  DumpSdata	will never be called from here.	*/
{
    register int  i;
    register char byte;
    int	 dummy;

    if (!Pass2)
	return;			/* Pass	2 only */
    if (HunkType == HunkBSS)
	return;			/* No data in BSS hunks! */

    if (HunkType == HunkNone) {		/* We're not in a hunk yet - */
	DoSection ("", 0, "", 0, "", 0);        /* start a code hunk */
	MakeHunk = TRUE;
    }

    if (OrgFlag) {		/* If we've had an ORG directive */
	FixOrg (dummy);		/*  do necessary adjustments	 */
	OrgFlag	= FALSE;	/*  to the object code file.	 */
    }

    Data <<= (4	- n) * 8;	/* Left-justify	data */

    for	(i = 0;	i < n; i++) {
	byte = (char) (Data >> ((3 - i)	* 8));
	TempAddr++;
	if (!SFormat) {
	    xputc (byte, &Srec);
	} else {
	    Sdata[Sindex++] = byte;
	    if (((TempAddr & 0x0F) == 0) || (Sindex >= MAXSREC))
		DumpSdata (&Srec);	/* Break S-record */
	}
    }
}



FixOrg (dummy) int dummy;
/* Makes necessary adjustments to the object code file if an
    ORG	directive has been processed.  This routine is called
    exclusively	by AppendSdata and must	only be	called once for
    each ORG encountered, when writing the next	object code (if	any). */
{
    register long templong;
    register int  i;

    if (SFormat	&& (AddrCnt != TempAddr)) {	/* ORG in S-format -	*/
	DumpSdata (&Srec);			/*  dump current record	*/
	StartAddr = TempAddr = AddrCnt;		/*  and	start afresh.	*/
    }
    if (AddrCnt	< TempAddr) {		/* AmigaDOS backward ORG */
	if (TempAddr > OrgHigh)	{
	    LenPtr = NULL;
	    OrgHigh = TempAddr;	/* Save	high address for return	*/
	    if (Srec.Ptr > Srec.Buf)	/* Flush the buffer */
		write (Srec.fd,	Srec.Buf, Srec.Ptr - Srec.Buf);
	    OrgSeek = lseek (Srec.fd, 0L, 1);	/* Remember position */
	    lseek (Srec.fd,(AddrCnt & ~3L)-TempAddr,1);	/* New position	*/
	    if (AddrCnt	& 3L) {		/* If ORG isn't to long-word   */
		read (Srec.fd, Srec.Buf, AddrCnt & 3L);	/*  move ahead */
		lseek (Srec.fd,	-(AddrCnt & 3L), 1);	/*  to keep    */
	    }						/*  the	buffer */
	    Srec.Ptr = Srec.Buf	+ (AddrCnt & 3L);	/*  aligned.   */
	}
	StartAddr = TempAddr = AddrCnt;

    } else if (AddrCnt > TempAddr) {	/* AmigaDOS forward ORG	*/
	if (OrgHigh > TempAddr)	{	/* Previous backward ORG */
	    if (AddrCnt	< OrgHigh)
		templong = AddrCnt;	/* Within previous range */
	    else
		templong = OrgHigh;	/* Beyond previous range */
	    i =	(int) (templong	& 3L);	/* Alignment factor */
	    templong -=	TempAddr;	/* Number of bytes to skip */
	    LenPtr = NULL;
	    if (Srec.Ptr > Srec.Buf)	/* Flush the buffer */
		write (Srec.fd,	Srec.Buf, Srec.Ptr - Srec.Buf);
	    lseek (Srec.fd,templong-(long)i,1);	/* Skip	written	data */
	    if (i) {		/* If skip isn't to long-word, */
		read(Srec.fd,Srec.Buf,(long)i);	/*  move ahead */
		lseek(Srec.fd, -((long)	i), 1);	/*  to keep    */
	    }					/*  the	buffer */
	    Srec.Ptr = Srec.Buf	+ (long) i;	/*  aligned.   */
	    TempAddr +=	templong;
	    StartAddr =	TempAddr;
	}
	while (TempAddr	< AddrCnt) {	/* Extend with binary zeros */
	    xputc (0, &Srec);
	    TempAddr++;
	}
    }
}



DumpSdata (f) register struct fs *f;
/* Writes an object code record	*/
{
    register long CheckSum, templong;
    register char *s;
    register int  i;

    if (!SFormat) {
	if (AddrCnt < OrgHigh) {	/* If we did a backwards ORG */
	    LenPtr = NULL;		/*  we have to fix things up */
	    if (f->Ptr > f->Buf)	/* Flush the buffer */
		write (f->fd, f->Buf, f->Ptr - f->Buf);
	    lseek(f->fd,OrgSeek&~3L,0);	/* Back	to high	position */
	    if (OrgSeek	& 3L) {		/* If ORG isn't to long-word,  */
		read (f->fd, f->Buf, OrgSeek & 3L);	/*  move ahead */
		lseek (f->fd, -(OrgSeek	& 3L), 1);	/*  to keep    */
	    }						/*  the	buffer */
	    f->Ptr = f->Buf + (OrgSeek & 3L);		/*  aligned.   */
	    AddrCnt = StartAddr	= TempAddr = OrgHigh;
	}
	AddrCnt	= AddrBndL (AddrCnt);	/* Finish the last long	word */
	templong = ((AddrCnt - SectStart) >> 2)	| HunkFlags;
	if ((s = LenPtr) == NULL) {
	    if (f->Ptr > f->Buf)		/* Flush the buffer */
		write (f->fd, f->Buf, f->Ptr - f->Buf);
	    CheckSum = lseek (f->fd, 0L, 1);	/* Remember position */
	    lseek (f->fd, LenPos, 0);		/* Put hunk length here	*/
	    s =	f->Buf;
	}
	*s++ = (char) (templong	>> 24);
	*s++ = (char) (templong	>> 16);
	*s++ = (char) (templong	>> 8);
	*s++ = (char) templong;
	if (LenPtr == NULL) {
	    write (f->fd, f->Buf, 4);
	    lseek (f->fd, CheckSum, 0);	/* Back	to where we were */
	    f->Ptr = f->Buf;
	}
	DumpRel	(f);		/* Write relocation information	*/
	templong = HunkEnd;
	xputl (f, templong);	/* End of the hunk */
	TempAddr = AddrCnt;
	return;
    }

    if (Sindex == 0)
	 return;		/* There's nothing to dump */

    xputs (f, "S2");
    templong = Sindex +	4;	/* Record length */
    LongPut (f,	templong, 1);
    CheckSum = templong;	/* Initialize CheckSum */

    LongPut (f,	StartAddr, 3);	/* Address */
    CheckSum +=	(StartAddr >> 16) & 0x00FFL;
    CheckSum +=	(StartAddr >> 8) & 0x00FFL;
    CheckSum +=	StartAddr & 0x00FFL;

    for	(i = 0;	i < Sindex; i++) {
	templong = Sdata[i];
	LongPut	(f, templong, 1);	/* Object code */
	CheckSum += templong;
    }
    CheckSum = ~CheckSum;	/* Complement checksum */
    LongPut (f,	CheckSum, 1);
    xputs (f, "\n");

    StartAddr += Sindex;
    TempAddr = StartAddr;
    Sindex = 0;
}



PutRel (addr, hunk, size) long addr, hunk; int size;
/* Build a relocation entry if necessary */
{
    register struct RelTab *rel;

    if (!Pass2)
	return;				/* Pass	2 only */
    if (SFormat)
	return;				/* Not for S-format! */
    if (hunk ==	ABSHUNK)
	return;				/* Absolute */
    if (HunkType == HunkBSS)
	return;				/* Not for BSS hunks! */

    rel	= RelLim;			/* Pointer to new entry	*/
    RelLim++;				/* Bump	limit pointer */
    if (((char *) RelLim - (char *) RelCurr) > CHUNKSIZE) {
	rel = (struct RelTab *)	malloc ((unsigned) CHUNKSIZE);
	if (rel	== NULL)
	    quit_cleanup ("Out of memory!\n");
	RelCurr->Link =	rel;		/* Link	from previous chunk */
	RelCurr	= rel;			/* Make	the new	chunk current */
	RelCurr->Link =	NULL;		/* Clear forward pointer */
	rel++;				/* Skip	over pointer entry */
	RelLim = rel;			/* New table limit */
	RelLim++;			/* Bump	it */
    }
    if (RelLast	!= NULL)
	RelLast->Link =	rel;		/* Link	from previous entry */
    rel->Link =	NULL;			/* End of the chain (so	far) */
    rel->Offset	= addr;			/* Offset */
    rel->Hunk =	hunk;			/* Hunk	number */
    rel->Size =	size;			/* Size	*/
    RelLast = rel;			/* Pointer to last entry in chain */

    if (hunk < 0)			/* Count entries by type */
	NumRExt++;
    else if (size == Long)
	NumR32++;
    else if (size == Word)
	NumR16++;
    else
	NumR8++;
}



DumpRel	(f) struct fs *f;
/* Dump	relocation information to the object file. */
{
    register struct SymTab *sym;
    register struct RelTab *rel, *rel2;
    int	 i, j, size, num, donexhdr, secthlin;
    long currhunk, nexthunk, templong;
    char *p;

    secthlin = LineCount;		/* Current section ends	here */
    if ((Dir ==	Section)		/*   unless we're starting   */
    || (Dir == CSeg)			/*	a new section.	     */
    || (Dir == DSeg)
    || (Dir == BSS))
	secthlin--;			/* Then	it ends	at previous line */

    if (SFormat)
	return;				/* S-format is absolute! */

    while (1) {
	if ((num = NumR32) != 0) {
	    size = Long;		/* Do 32-bit fields */
	    templong = HunkR32;
	    NumR32 = 0;			/* ...but only once */
	} else if ((num	= NumR16) != 0)	{
	    size = Word;		/* Then	do 16-bit fields */
	    templong = HunkR16;
	    NumR16 = 0;
	} else if ((num	= NumR8) != 0) {
	    size = Byte;		/* Finally do 8-bit fields */
	    templong = HunkR8;
	    NumR8 = 0;
	} else
	    break;			/* We're all done */

	xputl (f, templong);		/* Record type */

	currhunk = 32767;
	num = 0;
	if (rel	= RelStart)	/* If we have anything,	*/
	    rel++;		/*  skip over the first	chunk's link. */
	while (rel) {
	    if ((rel->Size == size) && (rel->Hunk >= 0)) {
		if (rel->Hunk <	currhunk) {
		    currhunk = rel->Hunk;	/* Lowest hunk number */
		    num	= 1;			/* Reset counter */
		} else if (rel->Hunk ==	currhunk) {
		    num++;			/* Count entries */
		}
	    }
	    rel	= rel->Link;
	}
	while (num > 0)	{	/* Repeat for all hunk references */
	    templong = num;
	    xputl (f, templong);	/* Number of entries */
	    xputl (f, currhunk);	/* Hunk	number */
	    nexthunk = 32767;
	    num	= 0;			/* Count for next hunk */
	    if (rel = RelStart)
		rel++;
	    while (rel)	{
		if ((rel->Size == size)	&& (rel->Hunk >= 0)) {
		    if (rel->Hunk < currhunk) {
			rel = rel->Link;	/* Already wrote it */
			continue;
		    } else if (rel->Hunk == currhunk) {
			xputl (f, rel->Offset -	SectStart);
		    } else if (rel->Hunk < nexthunk) {
			nexthunk = rel->Hunk;	/* Next	hunk number */
			num = 1;		/* Reset counter */
		    } else if (rel->Hunk == nexthunk) {
			num++;			/* Count entries */
		    }
		}
		rel = rel->Link;
	    }
	    currhunk = nexthunk;	/* Get ready for next hunk */
	}
	xputl (f, 0L);		/* End of relocation information */
    }

    donexhdr = FALSE;		/* Haven't written hunk_ext yet */

    sym	= SymChunk = SymStart;
    sym++;
    SymChLim = (struct SymTab *) ((char	*) SymChunk + CHUNKSIZE);
    while (sym)	{
	if (sym->Flags & 2) {		/* Scan	for XDEF symbols */
	    j =	sym->Defn;	/* Defined in current section? */
	    if ((j >= SectLine)	&& (j <= secthlin)) {
		if (!donexhdr) {
		    templong = HunkExt;	/* Haven't done header yet */
		    xputl (f, templong);
		    donexhdr = TRUE;
		}
		if ((sym->Hunk & 0x0000FFFFL) == ABSHUNK)
		    templong = 0x02000000;
		else
		    templong = 0x01000000;		/* Flags */
		DumpName (f, sym->Nam, templong);	/* Symbol */
		xputl (f, sym->Val - SectStart);	/* Offset */
	    }
	}
	sym = NextSym (sym);
    }

    if (NumRExt	!= 0) {			/* External references (XREF) */
	if (!donexhdr) {
	    templong = HunkExt;		/* Haven't done header yet */
	    xputl (f, templong);
	    donexhdr = TRUE;
	}
	if (rel	= RelStart)
	    rel++;
	while (rel) {
	    if (rel->Hunk < 0) {
		p = (char *) ~(rel->Hunk);
		size = rel->Size;
		if (size == Long)
		    templong = 0x81000000L;	/* ext_ref32 */
		else if	(size == Word)
		    templong = 0x83000000L;	/* ext_ref16 */
		else
		    templong = 0x84000000L;	/* ext_ref8 */
		DumpName (f, p,	templong);	/* Flags and symbol */
		templong = 1;
		rel2 = rel->Link;
		while (rel2) {
		    if ((rel2->Hunk == rel->Hunk) && (rel2->Size == size))
			templong++;		/* Number of times */
		    rel2 = rel2->Link;		/*  symbol occurs  */
		}
		xputl (f, templong);
		rel2 = rel;			/* Now go back and  */
		while (rel2) {			/*  write them out. */
		    if ((rel2->Hunk==rel->Hunk)	&& (rel2->Size==size)) {
			xputl(f, rel2->Offset -	SectStart); /* Offset */
			if (rel2 != rel)	/* Kill	hunk so	we    */
			    rel2->Hunk = 0;	/*  don't do it again */
		    }				/*  (we're done with  */
		    rel2 = rel2->Link;		/*  the	table anyway) */
		}
	    }
	    rel	= rel->Link;
	}
	NumRExt	= 0;
    }
    if (donexhdr)
	xputl (f, 0L);			/* End of external information */

    if (DumpSym) {			/* Dump	the symbol table */
	donexhdr = FALSE;
	sym = SymChunk = SymStart;
	sym++;
	SymChLim = (struct SymTab *) ((char *) SymChunk	+ CHUNKSIZE);
	while (sym) {
	    if ((sym->Hunk & 0x0000FFFFL) == CurrHunk) {
		j = sym->Flags & 0x7F;		/* Ignore PUBLIC flag */
		if ((j == 0) ||	(j == 2)) {	/* Defined, may	be XDEF	*/
		    if ((sym->Defn >= SectLine)	&& (sym->Defn <= secthlin)) {
			if (!donexhdr) {	/* In current SECTION */
			    templong = HunkSym;
			    xputl (f, templong);	/* Write header	*/
			    donexhdr = TRUE;		/* if necessary	*/
			}
			DumpName (f, sym->Nam, 0L);	/* Symbol */
			xputl(f, sym->Val - SectStart);	/* Offset */
		    }
		}
	    }
	    sym	= NextSym (sym);
	}
	if (donexhdr)
	    xputl (f, 0L);		/* End of symbol table dump */
    }

    rel	= RelStart->Link;
    while (rel != NULL)	{
	rel2 = rel;
	rel = rel2->Link;
	free (rel2);			/* Free	all but	the first chunk	*/
    }
    RelCurr = RelStart;			/* The first chunk is current */
    RelCurr->Link = NULL;		/* Unlink additional chunks */
    RelLast = NULL;			/* There are no	entries	left */
    RelLim = RelStart;
    RelLim++;				/* First unused	space */
}



EndSdata (f, addr) struct fs *f; long addr;
/* Write end record to object file */
{
    register long checksum, templong;

    if (SFormat) {
	DumpSdata (f);			/* Write any remaining data */
	xputs (f, "S804");              /* Record header */
	checksum = 4;
	LongPut	(f, addr, 3);		/* Transfer address */
	checksum += (addr >> 16) & 0x00FFL;
	checksum += (addr >> 8)	& 0x00FFL;
	checksum += addr & 0x00FFL;
	checksum = ~checksum;
	LongPut	(f, checksum, 1);	/* Checksum */
	xputs (f, "\n");
    } else {
	if (HunkType !=	HunkNone) {
	    DumpSdata (f);		/* Last	hunk's data */
	}
    }
}



DumpName (f, name, flags) struct fs *f;	char *name; long flags;
/* Writes a name preceded by a long word containing the
    length of the name in long words.  The length word has
    the	contents of "flags" ORed into it.  The name is padded
    with binary	zeros to the next long word boundary. */
{
    register int  i;
    register long templong;

    i =	strlen (name);
    templong = (i + 3) >> 2;	/* Length of name (long	words) */
    templong |=	flags;		/* Add flag bits */
    xputl (f, templong);	/* Write length	and flags */
    xputs (f, name);		/* Write the name itself */
    while (i & 3) {
	xputc ('\0', f);        /* Pad the last word */
	i++;
    }
}



LongPut	(f, data, length) struct fs *f;	long data; int length;
/* Writes to file "f" the hexadecimal interpretation of
    the	bytes in "data".  The number of bytes written
    (two hex digits per	byte) is given in "length" -
    if less than 4, only low-order bytes are written. */
{
    register int i, j;
    register char *t;
    char xstr[9];

    t =	xstr;
    for	(i = length * 8	- 4; i >= 0; i -= 4) {
	j = (int) ((data >> i) & 0x0FL);
	*t++ = (char) ((j > 9) ? (j - 10 + 'A') : (j + '0'));
    }
    *t = '\0';
    xputs (f, xstr);
}



xopen (name, f,	desc) char *name; struct fs *f;	char *desc;
/* Opens the output file whose name is in "name",
    setting up the file	structure pointed to by	"f".
    This routine first allocates a file	buffer -
    if unsuccessful, it	calls quit_cleanup.
    Otherwise, it opens	the file - if unsuccessful,
    displays an	error message using "desc" and returns TRUE.
    If the file	is successfully	opened,	this routine returns FALSE. */
{
    if ((f->Buf	= (char	*) malloc (BUFFSIZE)) == NULL)
	quit_cleanup ("Out of memory!\n");
    if ((f->fd = creat (name, 1)) == -1) {
	fprintf	(stderr, "Unable to open %s file.\n", desc);
	f->fd =	NULL;
	return (TRUE);
    }
    f->Ptr = f->Buf;
    f->Lim = f->Buf + BUFFSIZE;
    return (FALSE);
}



xputs (f, s) struct fs *f; register char *s;
/* Writes the string pointed to	by "s"
    to the output file whose structure is pointed to by	"f". */
{
    register char *t, *l;

    t =	f->Ptr;		/* Current position (use registers for speed) */
    l =	f->Lim;		/* End of buffer */

    while (*s) {
	*t++ = *s++;
	if (t >= l) {
	    write (f->fd, f->Buf, t - f->Buf);	/* Flush the buffer */
	    if (f == &Srec)
		LenPtr = NULL;	/* Hunk	length is no longer in buffer */
	    t =	f->Buf;				/* Reset pointer */
	}
    }
    f->Ptr = t;					/* Update pointer */
}



xputl (f, data)	register struct	fs *f; register	long data;
/* Writes to file "f" the contents of the long word in "data". */
{
    xputc ((char) (data	>> 24),	f);
    xputc ((char) (data	>> 16),	f);
    xputc ((char) (data	>> 8), f);
    xputc ((char) data,	f);
}



xputc (byte, f)	char byte; register struct fs *f;
/* Writes the byte contained in	"byte" to file "f". */
{
    register char *t;

    t =	f->Ptr;		/* Current position (use register for speed) */

    *t++ = byte;
    if (t >= f->Lim) {
	write (f->fd, f->Buf, t	- f->Buf);	/* Flush the buffer */
	if (f == &Srec)
	    LenPtr = NULL;	/* Hunk	length is no longer in buffer */
	t = f->Buf;				/* Reset pointer */
    }
    f->Ptr = t;					/* Update pointer */
}



xclose (f) struct fs *f;
/* Closes the output file whose	structure is pointed to	by "f".
    The	buffer is flushed if necessary,	then freed.		*/
{
    if (f->Ptr > f->Buf)
	write (f->fd, f->Buf, f->Ptr - f->Buf);	/* Flush the buffer */
    close (f->fd);				/* Close the file */
    f->fd = NULL;
    free (f->Buf);				/* Free	the buffer */
    f->Buf = NULL;
}



Error (pos, errornum) int pos, errornum;
/* Displays error message #errornum.  If this is the first error for
    the	current	line, the line itself is displayed, preceded by	a
    message giving the current position	in the current module.
    If the line	is in a	macro or include file, the position in
    each nested	module is given, working out to	the source file.
    A flag is placed under the column indicated	by "pos".       */
{
    register int i;
    int	dummy;

    if (!Pass2 && (errornum != NoIncl))	{
	if (IncStart !=	0) {			/* Don't skip this     */
	    IncStart = 0;			/*  INCLUDE file in    */
	    if (SkipLim->Set1 != NULL) {	/*  pass 2 - we	must   */
		SetFixLim = SkipLim->Set1;	/*  re-read it to      */
		SetFixLim++;			/*  report its errors. */
	    }
	}
	return;				/* Report during pass 2	only */
    }
    if (ErrLim < ERRMAX) {		/* Save	error data */
	ErrCode[ErrLim]	= errornum;
	ErrPos[ErrLim] = pos;
	ErrLim++;
    }
    if (ErrLim == 1)		/* If first error for this line	       */
	DisplayLine (dummy);	/*  display the	line and its number(s) */
    printf ("\t");
    for	(i = 0;	i < pos; i++)
	if (Line[i] == '\t')
	    printf ("\t");
	else
	    printf(" ");                /* Space over to error column */
    printf ("^ %s\n",errmsg[errornum]); /* Error flag and message */
    ErrorCount++;			/* Count errors	*/
}



DisplayLine (dummy) int	dummy;
/* Displays the	current	line and its position
    in all current files - used	by Error, etc. */
{
    register struct InFCtl *inf;
    register int i;

    printf ("\n");
    for	(i = InFNum, inf = InF;	i >= 0;	i--, inf++) {	/* Nested? */
	if (inf->UPtr == 0)
	    printf ("%s", inf->NPtr);           /* Module name */
	else
	    printf ("(user macro)");            /* In a user macro */
	printf (" line %d\n", inf->Line);       /* Line number in module */
    }
    printf ("%5d   %s\n", LineCount, Line);     /* The line itself */
}
SHAR_EOF
cat << \SHAR_EOF > History.log
       A 6 8 K	  M A I	N T E N	A N C E	   H I S T O R Y


Version	2.42 (Charlie Gibbs, January 10, 1989)

	The following bugs in version 2.41 have	been corrected:

	      -	Small code/data	conversion was sometimes taking	place
		when no	NEAR directive was active.  (Jeff Lydiatt)


Version	2.41 (Charlie Gibbs, January 6,	1989)

	The following bugs in version 2.4 have been corrected:

	      -	The second operand of LINK instructions	was
		being erroneously flagged.

	      -	If a macro was used before it was defined, it
		was being expanded during pass 2 but not during
		pass 1,	causing	severe phase errors.  Attempts
		to use a macro before it is defined will now
		be flagged as invalid opcodes.	(Colin Fox)


Version	2.4 (Charlie Gibbs, January 4, 1989)

	The following bugs in version 2.31 have	been corrected:

	      -	If comments immediately	followed the operands of
		a DC statement with no intervening white space,
		A68k would hang.  (Ulf Nordquist)

	      -	In the following command:
			a68k -w	15000 myprog.asm
		the space between the -w and 15000 would cause A68k
		to look	for a source file called "15000", and to think
		that the object	file is	to be called "myprog.asm".
		When it	can't find "15000" it would display an error
		message	and scratch "myprog.asm".  (Jeff Lydiatt)

	      -	If an INCLUDE file that	is skipped on pass 2 contains
		a macro	calls, subsequent uses of \@ (macro sequence
		number are subsequently	flagged.  The macro counter
		must be	bumped along with the line number when
		skipping an INCLUDE.  (Colin Fox, Harvey Taylor)

	The following enhancements have	been added:

	      -	ORG and	RORG are now fully implemented.

	      -	The SET	symbols	A68k, a68K, and	a68k are defined in the
		same way as A68K, making it effectively	case-insensitive.
		(Colin Fox)

	      -	MOVEM and REG now accept equated register names	(EQUR)
		in register lists.  (Bruce Dawson)

	      -	INCLUDE	files will now be skipped on pass 2 even when
		a listing file is requested, if	the listing has	been
		turned off by a	NOLIST directive before	the INCLUDE,
		and is not turned on until after the end of the
		INCLUDE	file has been reached.	(Colin Fox)

	      -	A new switch (-f) causes forward branches (Bcc,	BRA, BSR)
		that could be coded as short branches (Bcc.S etc.) to be
		flagged.  This flag is not considered to be an error.

	      -	A limited small	code / small data model	has been provided.
		It is activated	by a NEAR directive in the code, and is
		de-activated by	a FAR directive.  External variables must
		be declared at the beginning of	the program, which must
		consist	of only	two sections (CODE and DATA or BSS).
		All forward data references are	assumed	to be PC-relative
		if in the CODE section,	A4-relative if in the DATA/BSS
		section, and absolute word if absolute values.	Any
		forward	references which cannot	be resolved to one of
		these three in pass 2 will be flagged as errors, as will
		any attempt to define more than	two sections.  A4 is
		assumed	to point to the	start of the DATA/BSS section
		plus 32768 bytes, and must be loaded by	a MOVE.L
		instruction using immediate mode unless	this instruction
		is not enclosed	within NEAR and	FAR directives.

	      -	Miscellaneous optimizations, for speed,	including:
		Most of	the object code	generator in pass 1 is bypassed.
		If GetValue gets a single term it takes	a short	cut.
		IsOperator now uses a table look-up.
		Instructions now only searches that portion of the
		opcode table whose opcodes start with the same letter
		as the OpCode being searched for.


Version	2.31 (Charlie Gibbs, November 30, 1988)

	The following bugs in version 2.3 have been corrected:

	      -	Even though a macro definition was being skipped
		by IFxx/ENDC, its ENDM directive was still being
		detected, causing spurious diagnostics.	 (Harvey Taylor)

	      -	NOP was	not being recognized.  When moving all
		directives into	the opcode table, NOL and NOLIST
		were placed after NOP, rather than before.  (Colin Fox)

	      -	Symbols	defined	in the current module and declared
		as PUBLIC were not being written to the	object code
		file when -d was specified.  (Colin Fox)

	      -	Conversion of 0(An) to (An) (implemented in version
		1.2) was causing errors	in the MOVEP instruction,
		which requires a displacement even if it is zero.
		This conversion	is now disabled	for MOVEP instructions.

	      -	User macros containing invalid opcodes caused A68k
		to get lost when returning to the outer	source file.
		(Colin Fox)

	      -	Large values of	-w (over 6000 or so) would cause
		a visit	from the Guru.	The work field in HashIt
		was overflowing	and going negative.  Changing it
		to unsigned corrected the problem.  (Colin Fox)

	      -	Although user macros are no longer displayed when
		-q is a	negative number, the calling file's name
		was still being	displayed at the end of	the macro.


Version	2.3 (Charlie Gibbs, November 21, 1988)

	The following enhancements have	been added:

	      -	All file I/O has been rewritten	to use level 1 I/O
		(open, creat, close, read, write, and lseek) instead
		of level 2 I/O.	 A68k now does its own buffering and
		unbuffering to reduce system overhead and increase speed.
		(Bruce Dawson)

	      -	All assembler directives have been incorporated	into
		the opcode table.  Since the opcode search now looks
		up directives as well, speed is	increased.

	      -	Miscellaneous code optimization	for additional speed.


Version	2.2 (Charlie Gibbs, November 4,	1988)

	The following bugs in version 2.1 have been corrected:

	      -	Macro definitions within an INCLUDE file were
		disabling the test for skipping	the file on pass 2.

	      -	Errors encountered in an INCLUDE file on pass 1
		were not disabling the skip of the file	on pass	2 -
		the pertinent error messages could not appear.

	      -	XDEF information and optional symbol table dumps were
		not being written to the object	code file for any
		hunks that did not contain relocatable code or data.
		(Colin Fox)

	The following enhancements have	been added:

	      -	If the -q option is specified as a negative value,
		user macros are	no longer included in line number
		displays, reducing clutter.

	      -	Some source code has been re-arranged to reduce	size.


Version	2.1 (Charlie Gibbs, November 1,	1988)

	The following bugs in version 2.00 have	been corrected:

	      -	Macro definitions that span two	chunks of memory
		were causing garbage and probably a crash when
		the macro was being expanded.  Pointers	were not
		being handled properly when linking the	two chunks.

	      -	Statements such	as EQU and SET were not	being flagged
		as illegal forward references if referencing a label
		defined	on the same line, e.g.
			LABEL	SET	LABEL+1

	      -	The position within macros and INCLUDE files was
		sometimes out by one line when reported	in error
		messages (and the new feature of the -q	switch).

	The following enhancements have	been added:

	      -	If the -q option is specified as a negative value,
		line numbers will be displayed as positions within
		the current module (whose name is also displayed),
		rather than a total statement count.  (Bruce Dawson)

	      -	INCLUDE	files can be skipped on	pass 2 even if they
		contain	SET statements - the values of all symbols
		SET in the INCLUDE file	are stored (as at the end
		of the file) in	a separate table and are patched
		when the INCLUDE file is skipped.  (Bruce Dawson)


Version	2.00 (Charlie Gibbs, October 26, 1988)

	The following bugs in version 1.24 have	been corrected:

	      -	The last digit of the statement	number display
		(lengthened in version 1.24) was not being erased
		before displaying error	messages.

	      -	A68k would go into a loop if a user macro was
		missing	an ENDC	directive.  This error is now
		flagged	(see below).

	The following enhancements have	been added:

	      -	The highest statement number displayed at the end of
		each pass is now left on the screen.  This means that,
		at the end of pass 1, you can always see how many lines
		A68k will have to process in pass 2, giving an idea of
		how how	much longer you	have to	wait.  (Colin Fox)

	      -	The symbol table is now	built using a hashing algorithm.
		This eliminates	the slowdown that occurs in pass 1 as
		the symbol table grows,	due to the old insertion process.
		(Bruce Dawson)

	      -	If A68k	terminates abnormally for any reason (such as
		insufficient memory) the object	file is	scratched
		(unless	the -k option is set).	(Bruce Dawson)

	      -	Any INCLUDE files which	cannot be found	are flagged
		as errors in pass 1, and the assembly is aborted
		at the end of pass 1.  (Bruce Dawson)

	      -	Missing	ENDC directives	are flagged in macro expansions.
		Also, missing or unpaired ENDC directives in user macros
		are flagged.

	      -	If an INCLUDE file doesn't generate any code and no
		listing	file is	required, it won't be read again in
		pass 2.	 The statement numbers will be bumped to keep
		in proper alignment.  This can really speed up
		assemblies that	INCLUDE	lots of	equates.  (Colin Fox)


Version	1.24 (Charlie Gibbs, October 11, 1988)

	The following bugs in version 1.23 have	been corrected:

	      -	MOVEA to a data	register was not being flagged,	even
		though all other invalid addressing modes were.

	      -	Attempts to ORG	out of the current hunk	(including
		to an absolute address)	were not being flagged.	 (E. Lenz)

	      -	If the size of the bottom of the primary heap (symbols
		and macro text)	exceeded 32K, any further macro
		definitions would expand as endless garbage.  (Colin Fox)

	      -	If the size of the bottom of the primary heap (symbols
		and macro text)	exceeded 64K, any further external
		symbols	(XDEF) would be	flagged	as relocatability
		errors upon each reference.  (Colin Fox)

	The following enhancements have	been added:

	      -	Where statement	numbers	are displayed as fixed-length
		fields,	their maximum length has been increased
		from 4 digits to 5.  (Colin Fox)

	      -	The PUBLIC directive has been implemented.
		As with	the Aztec assembler, any labels	defined	as
		PUBLIC will be treated as XDEF if defined within
		the current module, and	XREF otherwise.	 (Jeff Lydiatt)


Version	1.23 (Charlie Gibbs, September 20, 1988)

	The following bugs in version 1.22 have	been corrected:

	      -	The test for a third operand was producing erroneous
		error messages on instructions whose second operand
		was in immediate mode.	The '#' was not being taken
		into account, since it is not copied to	DestOp.


Version	1.22 (Charlie Gibbs, August 31,	1988)

	The following bugs in version 1.21 have	been corrected:

	      -	Expressions of the form	R-A, where R is	a relocatable
		term or	expression and A is an absolute	term or
		expression, were being flagged as relocation errors.
		This was due to	a bug in the routine which should
		(but did not) flag expressions of the form A-R.
		(David Ashley)

	      -	Instructions with three	operands were not being
		flagged	as errors.  This can be	caused by an extra
		comma being typed in the instruction, as in:
			BTST #0,state+3,(a5)
		The second comma should	not be present.	 (David	Ashley)

	The following enhancements have	been added:

	      -	Excess spacing has been	removed	from the listing file.
		These changes are similar to those already made	to the
		console	output (probably at about version 1.05).

	      -	If the first statement in the source file is TTL or
		PAGE, an empty page is no longer produced at the
		start of the listing.


Version	1.21 (Charlie Gibbs, July 29, 1988)

	The following bugs in version 1.2 have been corrected:

	      -	The instruction
			BTST.L	#8,D0
		had a long-word	value generated	for the	bit number.
		This bug also applies to BSET, BCLR, and BCHG.
		The .L specification is	now ignored.  (Ulf Nordquist)


Version	1.2 (Charlie Gibbs, July 19, 1988)

	The following bugs in version 1.12 have	been corrected:

	      -	A reference to the label of the	current	instruction
		was being converted to PC-relative on pass 2 but not
		on pass	1.  This was causing phase errors.  The	label
		hasn't been added to the symbol table at the time the
		instruction is processed.  Conversion to PC-relative
		addressing will	now not	be attempted in	this case,
		although references to * can and will be converted.

	      -	All string-type	DC statements, regardless of length,
		were being treated as DC.B.  For example, DC.L 'A'
		would generate only one	byte of	object code.
		(Gerald	Hull)

	      -	DC.W and DC.B statements were not being	checked	to
		ensure that their values would fit into	a word or
		a byte respectively.

	      -	If a comment line had white space preceding the
		asterisk, A68k would hang.  Actually, it was
		interpreting the asterisk as an	opcode and trying
		to open	a macro	file called "*".  Since under
		AmigaDOS such a	file is	the console, A68k was
		actually waiting for console input.

	      -	If an instruction with no operands (such as RTS
		or NOP)	followed MOVE.L	#rel,D0	where "rel" was
		a relocatable symbol, the RTS (etc.) would have
		its nonexistent	operands flagged as invalid.

	      -	SECTION	names enclosed in quotes were not being
		handled	correctly.

	      -	Source modules that did	not generate any code, data,
		or BSS areas, but only defined symbols,	such as
			label	equ	4
				xdef	label
				end
		were generating	incomplete object modules.

	The following enhancements have	been added:

	      -	Jeff's experimental hunk code (prefixing hunk names
		with a sequence	number before adding to	the symbol
		table) has been	permanently incorporated.  It seems
		to work	better with BLink on programs that have
		hunks continued	farther	on in the source code.
		(Jeff Lydiatt)

	      -	The macro parameter \0,	which is replaced by the
		size specification in the macro	call (B, W, or L,
		defaulting to W) is now	supported.  (Gerald Hull)

	      -	Operands of the	form 0(An) will	be treated as (An).
		(Bruce Dawson)


Version	1.12 (Charlie Gibbs, May 25, 1988)

	The following bugs in version 1.11 have	been corrected:

	      -	If an instruction with no operands (e.g. RTS)
		followed a MOVE.L #label,D0 the	RTS would be
		flagged	with a relocatability error.  Src.Mode
		and Dest.Mode were not being cleared.  (Colin Fox)


Version	1.11 (Charlie Gibbs, April 6, 1988)

	The following bugs in version 1.10 have	been corrected:

	      -	A68k would go into a loop while	processing the
		arguments of a macro call, if these arguments are
		followed by comments separated from the	arguments
		by one or more tab characters, and the -t switch
		is specified on	the command line.  All tests for
		blanks have been replaced by calls to isspace().

	      -	The operand alignment checks added in version 1.06
		were erroneously testing the following instructions:
			BCHG
			BCLR
			BSET
			BTST
			NBCD
			Scc
			TAS
		These instructions are now exempt from alignment checking.

	The following enhancements have	been added:

	      -	A listing file name can	now be specified with the
		-x switch; it is no longer necessary to	specify
		both the -l and	-x switches to produce a cross-
		reference listing with a name other than the default.

	      -	DS statements with more	than one operand are
		flagged	and ignored (in	case they should be DC).

	      -	A character string used	as a numeric value is
		flagged	and set	to zero	if it is more than four
		characters long.


Version	1.10 (Charlie Gibbs, March 20, 1988)

	The following bugs in version 1.07 have	been corrected:

	      -	BSS sections were not being written to the object
		code file except for a BSS section at the end of
		a program.  This is due	to a bug in the	code added
		in version 1.05	to overwrite null sections.

	      -	If a source module contained a mixture of lengths
		(8, 16,	or 32 bits) in external	references (XREF)
		to the same label, all references were being treated
		as if they has the length of the first reference.

	The following enhancements have	been added:

	      -	DS operands that are either a forward references
		or relocatable are now flagged.

	      -	Short branches (Bcc.S, including BRA and BSR) to
		the next instruction (i.e. a displacement of zero)
		are illegal - the processor takes the displacement
		from the next word.  Attempts to generate a short
		displacement of	zero are now flagged.


Version	1.07 (Charlie Gibbs, March 11, 1988)

	The following bugs in version 1.06 have	been corrected:

	      -	Instructions that take no operands (such as RTS)
		were being flagged if they had comments	that were
		not preceded by	a semicolon.

	The following enhancements have	been added:

	      -	The following synonyms have been added:
			CSEG  for CODE (Aztec compatibility)
			DSEG  for DATA	 "          "
			ENDIF for ENDC (Assempro compatibility)
			=     for EQU	   "           "
			|     for !	   "           "

	      -	Strings	and character values may be delimited by
		either apostrophes (') or quotation marks (").
		The character not used as a delimiter can be used
		within the string without doubling it.	For example,
			DC.B	"This is Charlie's assembler"
		produces the same code as
			DC.B	'This is Charlie''s assembler"

	      -	The object code	file will be scratched if any errors
		were found, unless the -k (keep) flag is set.
		(Bruce Dawson)

	      -	The symbol .A68K is automatically defined at the
		beginning of each assembly as a	SET symbol with	an
		absolute value of 1.  This enables programs to check
		whether	they're being assembled by this assembler.
		(Jeff Lydiatt)

	      -	The symbol table insertion routine has been
		greatly	speeded	up.


Version	1.06 (Charlie Gibbs, March 6, 1988)

	The following bugs in version 1.05 have	been corrected:

	      -	Lines skipped by IFxx/ENDC were	not being counted
		in the line number given in error messages.

	      -	DATA and BSS sections may be unnamed, or have names
		the same as CODE sections.  Honest, I thought section
		names had to be	unique even across types.

	      -	CHIP and FAST options on the CODE, DATA, and BSS
		synonyms for the SECTION directive were	not being
		handled	correctly.

	      -	XDEF records and symbol	table records (if desired)
		were not being produced	for symbols defined ahead
		of the first object-code producing instruction.

	The following enhancements have	been added:

	      -	The CNOP instruction can now force alignment
		relative to any	boundary up to 128 bytes.
		The second operand must	still be a power of 2.

	      -	The -q switch has been added to	change the frequency
		with which progress reports (current line number) are
		displayed on the console.  The default remains at
		every 10 lines (-q10).	If you specify -q (no interval)
		or -q0 the line	number displays	will be	suppressed.
		This will make assemblies run slightly faster due to
		reduced	console	I/O.  (Bill Henning)

	      -	The -t switch has been added to	keep any tabs in the
		source file when producing the listing file, as	well as
		generating tabs	elsewhere whenever possible.  This
		speeds up assemblies and gives smaller listing files,
		but such listing files cannot be displayed on devices
		that do	not assume a tab stop in every 8th position.
		(Bruce Dawson)

	      -	Any single-operand instruction with two	operands,
		and any	no-operand instruction with any	operands,
		will be	flagged.

	      -	Relocatable 8- or 16-bit immediate operands
		will be	flagged.  They blow up BLink.

	      -	Named local labels are now supported.  Their names
		are formed in the same way as normal labels, but are
		then preceded by a backslash.  Their scope is the
		same as	normal local labels (nnn$).  (Colin Fox)

	      -	An alignment error will	be flagged in the following cases:
		    Odd	displacement on	a LINK instruction
		    Bcc	or DBcc	to an odd address
		    In any word	or long-word instruction, any operand
		      using the	following addressing modes:
			Address	register indirect with displacement
			Address	register indirect with index and displacement
			Absolute short
			Absolute long
			Program	counter	indirect with displacement
			Program	counter	indirect with index and	displacement
		    LEA	and PEA	instructions are exempt	from these tests.

	      -	If a section is	found to contain no data, A68k will
		back up	to its beginning and overwrite it with the
		next section.  The result is that null sections
		will no	longer appear in the object file.


Version	1.05 (Charlie Gibbs, October 30, 1987)

	The following bugs in version 1.04 have	been corrected:

	      -	If a section was continued later in the	program, e.g.

			SECTION	prog,CODE
			  <code>
			SECTION	variables,BSS
			  <DS statements>
			SECTION	prog,CODE
			  <more	code>

		bad relocation information was being generated for
		the continuation of the	SECTION.  This bug was left
		over from version 1.03.

	The following enhancements have	been added:

	      -	All console output except for error messages is	now
		sent to	stderr - this enables stdout to	be redirected,
		producing an error file.

	      -	Console	(stderr) output	has been modified to require
		fewer lines on the screen.

	      -	If an error occurs while expanding a macro or INCLUDE
		file, the position of the call in each outer file is
		given along with the position in the current (innermost)
		file.  Tracing continues until the outermost file (i.e.
		the original source file) is reached.


Version	1.04 (Charlie Gibbs, October 21, 1987)

	The following bugs in version 1.03 have	been corrected:

	      -	MOVE was being converted to MOVEQ regardless of
		operand	size - this conversion is legal	only
		for longword MOVEs.

	      -	Modifications to version 1.03 caused bad relocatable
		entries	to be generated.


Version	1.03 (Charlie Gibbs, October 14, 1987)

	The following bugs in version 1.02 have	been corrected:

	      -	The following situation	was causing phase errors:

				xdef	label
				bra	label
				 .
			    <at	least 128 bytes	of object code>
				 .
			label:

		(The XDEF was fooling A68k into	thinking that "label"
		was defined within 128 bytes of	the BRA	instruction
		on pass	1, although on pass 2 it knew better.

	      -	If the first operand of	an two-operand executable
		instruction contained a	character term containing a
		left or	right parenthesis, it would generate error
		messages and be	incorrectly evaluated.

	      -	Labels that don't begin in column 1 (denoted by a
		trailing colon)	caused a Guru Meditation.

	      -	Certain	ADD and	SUB instructions using PC-relative
		addressing may cause phase errors.  If the displacement
		is in the range	1 to 8 inclusive, the instruction was
		erroneously converted to ADDQ or SUBQ during pass 2.

	The following enhancements have	been added:

	      -	The -z option has been added to	display	the
		current	source program line on stdout as it
		is read, optionally over a given range.
		This feature is	provided for debugging purposes.

	      -	Bcc, BSR, and DBcc to labels in	other than the current
		section	is now supported.  A 16-bit relocation entry
		will be	generated for each such	reference.

	      -	PC relative mode will be generated for backward
		references to labels within the	current	CODE section
		if legal for the current instruction.  Forward
		references will	not be converted, since	there is
		no way of telling which	section	the label is in
		during pass 1.

	      -	The cumulative sizes of	all sections by	type (i.e.
		CODE, DATA, and	BSS) will be displayed at the end
		of the listing file and	the console display.
		(Bruce Dawson)

	      -	In the symbol table dump, section names	will no
		longer be indicated just as SECTION, but rather
		as CODE, DATA, or BSS, depending on type.


Version	1.02 (Charlie Gibbs, September 9, 1987)

	The following bugs in version 1.01 have	been corrected:

	      -	Duplicate labels were not being	flagged.

	      -	XDEF symbols were not being dumped to the
		object code file when the -d option was	set.

	The following enhancements have	been added:

	      -	A header file is now supported.	 If the	parameter
		-h<filespec> is	included on the	command	line, the
		specified file will be included	as if the source
		file's first line was " include <filespec>".
		The file specification may include a path name,
		although the include path names	given by the
		-i parameter (if any) will also	be searched.

	      -	An equate file can now be produced.  If	the parameter
		-e<filespec> is	included on the	command	line, a	file
		will be	written	containing EQU statements for any
		symbol whose value is absolute.	 If -e is specified
		without	<filespec>, the	name of	the file will be
		formed in the same way as the list file, except	with
		an extension of	".equ".  (Bruce Dawson)

	The following changes have been	made to	existing logic:

	      -	No symbol table	dump will be produced unless the
		-x (cross-reference) switch is set.  Formerly a
		symbol table dump was always produced, with only
		the cross-reference portion optional.


Version	1.01 (Charlie Gibbs, August 20,	1987)

	The following bugs in version 1.00 have	been corrected:

	      -	Long-word constants and	storage	areas were being
		aligned	on a double-word boundary.  The	only place
		where double-word alignment is now forced is at	a
		break between SECTIONs,	since the length of an
		AmigaDOS hunk must be a	multiple of 4 bytes.
		(CNOP 0,4 can still be used if double-word
		alignment is desired by	the programmer.)

	      -	If a label on an END statement or the first statement
		of a SECTION was named in an XDEF statement, it	would
		not be written to the object code file.	 The latter
		case includes both the label of	a SECTION directive
		and the	label of the first executable instruction in
		the absence of any SECTION directives (defaulting to
		an unnamed CODE	section).  In the final	case (default
		unnamed	CODE section), references to XREF symbols
		in the first statement would also not be written
		to the object code file.

	      -	If the last statement in the source file was not
		terminated with	a newline character (premature EOF),
		it was being ignored altogether.

	      -	A register list	as the source operand of a MOVE
		instruction was	not being flagged as an	error.
		(MOVE to a register list was being flagged, however.)

	      -	MOVE from USP was generating incorrect code.  Also,
		MOVE from SR or	CCR to an address register was
		generating incorrect code rather than being flagged.


Version	1.00 (Charlie Gibbs, June 18, 1987) - initial release
SHAR_EOF
cat << \SHAR_EOF > A68k2do.txt
A68k - things to do as of January 10, 1989


*** BUGS ***

(There are no known bugs at this time.)


*** ENHANCEMENTS ***

Implement an ARexx interface to	an editor (e.g.	CED) for debugging.
Possibly commands could	be read	from a configuration file so that
A68k could be customized to any	editor's commands and port name.

Improve	the small code / small data option - study linker documentation!

Wild optimizations? (e.g. MOVE.W #0,D0 -> CLR.W	D0)

Treat (An,Dn) as 0(An,Dn)?  Or just leave for 68020 upgrade?

Add 68010/68020	support.
SHAR_EOF
cat << \SHAR_EOF > Makefile
# Simple makefile to compile and link the A68k Assembler.
# Created 23Jun87 by J.A. Lydiatt
#

OBJ=	A68kmain.o Opcodes.o Operands.o Adirect.o A68kmisc.o\
	Symtab.o Codegen.o wb_parse.o

SHARLIB1= A68k.doc makefile A68kdef.h A68kglb.h A68kmain.c Opcodes.c Operands.c\
	Adirect.c A68kmisc.c Symtab.c Codegen.c wb_parse.c isspace.c

SRC=	A68kmain.c Opcodes.c Operands.c Adirect.c A68kmisc.c\
	Symtab.c Codegen.c wb_parse.c isspace.c

SHARLIB2= A68k.uue

.c.o:
	cc +x3 -Z3000 -o $*.o $*.c

A68k:	$(OBJ) 
	ln -o A68k $(OBJ) -lc

$(OBJ):	A68kdef.h A68kglb.h

#
# Note: Manx's makefile seems to have a bug that forbids indirection
#	such as "shar >lib a.c  b.c c.c"
#	however "make >lib" seems to work fine if you edit out the
#	commands make echos as it proceeds through the makefile.
#
#	Try make >ram:shardcp Archive 
#

Archive:
	shar $(SHARLIB1)
	shar $(SHARLIB2)

Lint:
	lint -iAztec:include Aztec:Stdlib.c Aztec:Defaults.c $(SRC)
SHAR_EOF
cat << \SHAR_EOF > Makefile.azt
# Simple makefile to compile and link the A68k Assembler.
# Created 23Jun87 by J.A. Lydiatt
#

OBJ=	A68kmain.o Opcodes.o Operands.o Adirect.o A68kmisc.o\
	Symtab.o Codegen.o wb_parse.o

SHARLIB1= A68k.doc makefile A68kdef.h A68kglb.h A68kmain.c Opcodes.c Operands.c\
	Adirect.c A68kmisc.c Symtab.c Codegen.c wb_parse.c isspace.c

SRC=	A68kmain.c Opcodes.c Operands.c Adirect.c A68kmisc.c\
	Symtab.c Codegen.c wb_parse.c isspace.c

SHARLIB2= A68k.uue

.c.o:
	cc +x3 -Z3000 -o $*.o $*.c

A68k:	$(OBJ) 
	ln -t -o A68k $(OBJ) -lc

$(OBJ):	A68kdef.h A68kglb.h

#
# Note: Manx's makefile seems to have a bug that forbids indirection
#	such as "shar >lib a.c  b.c c.c"
#	however "make >lib" seems to work fine if you edit out the
#	commands make echos as it proceeds through the makefile.
#
#	Try make >ram:shardcp Archive 
#

Archive:
	shar $(SHARLIB1)
	shar $(SHARLIB2)

Lint:
	lint -iAztec:include Aztec:Stdlib.c Aztec:Defaults.c $(SRC)
SHAR_EOF
cat << \SHAR_EOF > Makefile.dbg
# Simple makefile to compile and link the A68k Assembler.
# Created 23Jun87 by J.A. Lydiatt
#

OBJ=	A68kmain.o Opcodes.o Operands.o Adirect.o A68kmisc.o\
	Symtab.o Codegen.o wb_parse.o isspace.o

SHARLIB1= A68k.doc makefile A68kdef.h A68kglb.h A68kmain.c Opcodes.c Operands.c\
	Adirect.c A68kmisc.c Symtab.c Codegen.c wb_parse.c isspace.c

SRC=	A68kmain.c Opcodes.c Operands.c Adirect.c A68kmisc.c\
	Symtab.c Codegen.c wb_parse.c isspace.c

SHARLIB2= A68k.uue

.c.o:
	cc -n -Z3000 -o $*.o $*.c

A68k:	$(OBJ)
	ln -g -o A68k $(OBJ) -lc

$(OBJ):	A68kdef.h A68kglb.h

#
# Note: Manx's makefile seems to have a bug that forbids indirection
#	such as "shar >lib a.c  b.c c.c"
#	however "make >lib" seems to work fine if you edit out the
#	commands make echos as it proceeds through the makefile.
#
#	Try make >ram:shardcp Archive 
#

Archive:
	shar $(SHARLIB1)
	shar $(SHARLIB2)

Lint:
	lint -iAztec:include Aztec:Stdlib.c Aztec:Defaults.c $(SRC)
SHAR_EOF
cat << \SHAR_EOF > Makefile.pdc
# Simple makefile to compile and link the A68k Assembler.
# Created 23Jun87 by J.A. Lydiatt
#

OBJ=	A68kmain.o Opcodes.o Operands.o Adirect.o A68kmisc.o\
	Symtab.o Codegen.o wb_parse.o isspace.o

SHARLIB1= A68k.doc makefile A68kdef.h A68kglb.h A68kmain.c Opcodes.c Operands.c\
	Adirect.c A68kmisc.c Symtab.c Codegen.c wb_parse.c isspace.c

SRC=	A68kmain.c Opcodes.c Operands.c Adirect.c A68kmisc.c\
	Symtab.c Codegen.c wb_parse.c isspace.c

SHARLIB2= A68k.uue

.c.o:
	cc -P -qvd0: -c $*.c

A68k:	$(OBJ) 
	cc -o A68k $(OBJ)

$(OBJ):	A68kdef.h A68kglb.h

#
# Note: Manx's makefile seems to have a bug that forbids indirection
#	such as "shar >lib a.c  b.c c.c"
#	however "make >lib" seems to work fine if you edit out the
#	commands make echos as it proceeds through the makefile.
#
#	Try make >ram:shardcp Archive 
#

Archive:
	shar $(SHARLIB1)
	shar $(SHARLIB2)

Lint:
	lint -iAztec:include Aztec:Stdlib.c Aztec:Defaults.c $(SRC)
SHAR_EOF
cat << \SHAR_EOF > wb_parse.c
_wb_parse(){}
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.