/*------------------------------------------------------------------*/
/*								    */
/*		      MC68000 Cross Assembler			    */
/*								    */
/*	      Copyright	(c) 1985 by Brian R. Anderson		    */
/*								    */
/*	      Miscellaneous routines - March 20, 1988		    */
/*								    */
/*   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 (c) 1988 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.",
    "Too many nested INCLUDEs.",
    "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.",
    "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(), GetField(), Instructions(), ObjDir();
extern int  GetSize(), GetInstModeSize(), GetMultReg();
extern int  ReadSymTab(), GetArgs(), GetAReg(),	OpenIncl();
extern long GetValue(),	CalcValue();
extern char *malloc();
extern FILE *fopen();

long AddrBndW(), AddrBndL();



long AddrBndW (v) long v;
/* Advances "v" to the next word boundary */
{
    if (v & 1L)	{
	AppendSdata (Srec, 0L, 1);
	v++;
    }
    return (v);
}



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

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



WriteListLine (f) FILE *f;
/* Writes one line to the Listing file,	including Object Code */
{
    register int i, j, printed;
    long templong;
    char macflag;

    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)
	    fprintf (f,	"  ");
    } else
	if (!KeepTabs)
	    fprintf (f,	"        ");    /* Don't print location */
    if (KeepTabs)
	fprintf	(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) {
	fprintf	(f, " ");
	LongPut	(f, ObjSrc, nS);
	printed	+= nS *	2 + 1;
    }
    if (nD != 0) {
	fprintf	(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) {
	    fprintf (f,	"\t");
	    printed += 8;
	    printed &= ~7;
	} else {
	    fprintf (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 */

    fprintf (f,	"  %5d%c%s\n", LineCount, macflag, Line);

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



WriteSymTab (f)	FILE *f;
/* Lists symbol	table in alphabetical order */
{
    int	 printhunk, i;
    char *p;
    register int j, k;
    struct SymTab *sym;
    struct Ref	  *ref;

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

	p = sym->Nam;			/* Pointer to symbol */
	while (*p == ' ')               /* Skip leading blanks, if any */
	    p++;
	if (sym->Flags & 0x10)
	    p++;			/* Skip	hunk type flag in symbol */
	fprintf	(f, "%-11s ", p);       /* Symbol or macro name */
	if (strlen (p) > 11)		/* Long	symbol - go to new line	*/
	    if (KeepTabs)
		fprintf	(f, "\n\t    ");
	    else
		fprintf	(f, "\n            ");

	printhunk = FALSE;		/* Assume no hunk no. to print */
	if (sym->Defn == NODEF)
	    fprintf (f,	"  *** UNDEFINED *** ");
	else if	(sym->Flags & 4)
	    fprintf (f,	"  -- SET Symbol --  ");
	else if	(sym->Flags & 8)
	    fprintf (f,	" +++ MACRO +++  %4d", sym->Defn);
	else if	(sym->Flags & 0x10) {
	    j =	(sym->Hunk & 0x3FFF0000L) >> 16;
	    if (j == HunkCode)
		fprintf	(f, "  CODE    ");
	    else if (j == HunkData)
		fprintf	(f, "  DATA    ");
	    else
		fprintf	(f, "  BSS     ");
	    printhunk =	TRUE;
	} else if (sym->Flags &	0x20) {
	    fprintf (f,	"      %c", (sym->Val & 8) ? 'A' : 'D');
	    fprintf (f,	"%d  ", sym->Val & 7);
	    printhunk =	TRUE;
	} else {
	    LongPut (f,	sym->Val, 4);		/* Value */
	    fprintf (f,	"  ");
	    printhunk =	TRUE;
	}
	if (printhunk) {
	    j =	sym->Hunk & 0x00007FFFL;	/* Hunk	number */
	    if (sym->Flags & 0x60)
		fprintf	(f, " Reg");            /* Register or list */
	    else if (sym->Hunk & 0x00008000L)
		fprintf	(f, " Ext");            /* External */
	    else if (j == ABSHUNK)
		fprintf	(f, " Abs");            /* Absolute */
	    else
		fprintf	(f, "%4d", j);          /* Hunk number */
	    fprintf (f,	"  %4d", sym->Defn);    /* Statement no. */
	}
	if (XrefList) {
	    fprintf (f,	"  ");
	    if (sym->Ref1 == 0)
		fprintf	(f, " *** UNREFERENCED ***");
	    else {
		ref = sym->Ref1;
		j = k =	0;
		while (1) {
		    if (ref->RefNum[j] == 0)
			break;
		    if (k >= 9)	{
			fprintf	(f, "\n");              /* New line */
			if (KeepTabs)
			    fprintf (f,	"\t\t\t\t  ");
			else
			    for	(k = 0;	k < 34;	k++)
				fprintf	(f, " ");
			k = 0;
		    }
		    fprintf (f,	" %4d", ref->RefNum[j]);
		    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	*/
		}
	    }
	}
	fprintf	(f, "\n");
    }
}



CheckPage (f, xhdr) FILE *f; int xhdr;
/* Checks if end of page reached yet --	if so, advances	to next	page. */
{
    register int printed;

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



StartSrec (f, idntname)	FILE *f; char idntname[];
/* Writes object header	record */
{
    register int i;
    long CheckSum, templong;

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

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

	for (i = 0; idntname[i]	!= '\0'; i++) {
	    templong = toupper (idntname[i]);
	    LongPut (f,	templong, 1);
	    CheckSum +=	templong;
	}
	CheckSum = ~CheckSum;		/* Complement checksum */
	LongPut	(f, CheckSum, 1);
	fprintf	(f, "\n");
    } else {
	templong = HunkUnit;
	putl (f, templong);
	DumpName (f, idntname, 0L);
    }
    StartAddr =	TempAddr = Sindex = 0;
    NumRExt = NumR32 = NumR16 =	NumR8 =	0;
}



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

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

    if (nO + nS	+ nD + nX) {		/* If we have object code */
	if (AddrCnt < TempAddr)	{	/*  and	location counter jumped	*/
	    if (SFormat)		/*  and	we're making S-format, */
		DumpSdata (f);		/*  get	rid of what we have */
	    StartAddr =	TempAddr = AddrCnt;	/*  and	start afresh. */
	}
	while (AddrCnt >= (TempAddr + 4))	/* It jumped forward - */
	    AppendSdata	(f, 0L,	4);		/*  fill with zeros    */
	if (AddrCnt > TempAddr)	{
	    i =	AddrCnt	- TempAddr;
	    AppendSdata	(f, 0L,	i);
	}
	AppendSdata (f,	ObjOp, nO);		/* Op code */
	AppendSdata (f,	ObjSrc,	nS);		/* Source */
	AppendSdata (f,	ObjDest, nD);		/* Destination */
	for (i = 0; i <	nX; i++) {		/* String data */
	    templong = ObjString[i];
	    AppendSdata	(f, templong, 1);
	}
    }
}



AppendSdata (f,	Data, n) FILE *f; 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;
    long templong;

    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;
    }
    Data <<= (4	- n) * 8;	/* Left-justify	data */

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



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

    if (!SFormat) {
	AddrCnt	= AddrBndL (AddrCnt);	/* Finish last long word */
	templong = ftell (f);
	fseek (f, LenPos, 0);	/* Hunk	length field is	here */
	putl (f, ((AddrCnt - SectStart)	>> 2) |	HunkFlags);
	fseek (f, templong, 0);	/* Back	to end of file */
	DumpRel	(f);		/* Write relocation information	*/
	templong = HunkEnd;
	putl (f, templong);	/* End of the hunk */
	TempAddr = AddrCnt;
	return;
    }

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

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

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

    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);
    fprintf (f,	"\n");

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



PutRel (addr, hunk, size) long addr, hunk; int size;
/* Build a relocation entry if necessary */
{
    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! */

    HeapSpace (sizeof(struct RelTab));	/* Make	sure we	have room */
    rel	= (struct RelTab *) HeapLim;	/* Pointer to new entry	*/
    rel->Offset	= addr;			/* Offset */
    rel->Hunk =	hunk;			/* Hunk	number */
    rel->Size =	size;			/* Size	*/
    HeapLim += sizeof(struct RelTab);	/* Bump	heap limit pointer */

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



DumpRel	(f) FILE *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;

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

    secthlin = LineCount;		/* Current section ends	here */
    if (strcmp (OpCode,"SECTION") == 0) /*   unless we're starting   */
	secthlin--;			/*	a new section.	     */

    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 */

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

	currhunk = 32767;
	num = 0;
	for (rel = RelStart; rel < (struct RelTab *) HeapLim; 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 */
		}
	    }
	}
	while (num > 0)	{	/* Repeat for all hunk references */
	    templong = num;
	    putl (f, templong);		/* Number of entries */
	    putl (f, currhunk);		/* Hunk	number */
	    nexthunk = 32767;
	    num	= 0;			/* Count for next hunk */
	    for	(rel = RelStart; rel < (struct RelTab *) HeapLim; rel++) {
		if ((rel->Size == size)	&& (rel->Hunk >= 0)) {
		    if (rel->Hunk < currhunk)
			continue;		/* Already wrote it */
		    else if (rel->Hunk == currhunk)
			putl (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 */
		    }
		}
	    }
	    currhunk = nexthunk;	/* Get ready for next hunk */
	}
	putl (f, 0L);		/* End of relocation information */
    }

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

    for	(i = 0,	sym = SymStart;	i < NumSyms; i++, 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 */
		    putl (f, templong);
		    donexhdr = TRUE;
		}
		if ((sym->Hunk & 0x0000FFFFL) == ABSHUNK)
		    templong = 0x02000000;
		else
		    templong = 0x01000000;		/* Flags */
		DumpName (f, sym->Nam, templong);	/* Symbol */
		putl (f, sym->Val - SectStart);		/* Offset */
	    }
	}
    }

    if (NumRExt	!= 0) {			/* External references (XREF) */
	if (!donexhdr) {
	    templong = HunkExt;		/* Haven't done header yet */
	    putl (f, templong);
	    donexhdr = TRUE;
	}
	for (rel = RelStart; rel < (struct RelTab *) HeapLim; rel++) {
	    if (rel->Hunk < 0) {
		p = Heap + ~rel->Hunk;
		size = rel->Size;
		if (size == Long)
		    templong = 0x81000000;	/* ext_ref32 */
		else if	(size == Word)
		    templong = 0x83000000;	/* ext_ref16 */
		else
		    templong = 0x84000000;	/* ext_ref8 */
		DumpName (f, p,	templong);	/* Flags and symbol */
		templong = 1;
		for (rel2 = rel	+ 1; rel2 < (struct RelTab *) HeapLim; rel2++)
		    if ((rel2->Hunk == rel->Hunk) && (rel2->Size == size))
			templong++;		/* Number of times */
		putl (f, templong);		/*  symbol occurs  */
		for (rel2 = rel; rel2 <	(struct	RelTab *) HeapLim; rel2++) {
		    if ((rel2->Hunk == rel->Hunk) && (rel2->Size == size)) {
			putl (f, rel2->Offset -	SectStart); /* Offset */
			if (rel2 != rel)	/* Kill	hunk so	we    */
			    rel2->Hunk = 0;	/*  don't do it again */
		    }				/*  (we're done with  */
		}				/*  the	table anyway) */
	    }
	}
	NumRExt	= 0;
    }
    if (donexhdr)
	putl (f, 0L);			/* End of external information */

    if (DumpSym) {			/* Dump	the symbol table */
	donexhdr = FALSE;
	for (i = 0, sym	= SymStart; i <	NumSyms; i++, sym++) {
	    if (((sym->Hunk & 0x0000FFFFL) == CurrHunk)
	    && ((sym->Flags == 0) || (sym->Flags == 2))) {
		j = sym->Defn;		/* Defined in current section? */
		if ((j >= SectLine) && (j <= secthlin))	{
		    if (!donexhdr) {
			templong = HunkSym;		/* Header */
			putl (f, templong);
			donexhdr = TRUE;
		    }
		    DumpName (f, sym->Nam, 0L);		/* Symbol */
		    putl (f, sym->Val -	SectStart);	/* Offset */
		}
	    }
	}
	if (donexhdr)
	    putl (f, 0L);		/* End of symbol table dump */
    }

    if (HeapLim	> HighHeap)
	HighHeap = HeapLim;		/* High-water mark */
    HeapLim = (char *) RelStart;	/* Reset heap limit */
}



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

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



DumpName (f, name, flags) FILE *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;

    templong = (strlen (name) +	3) >> 2;	/* Length (long	words) */
    templong |=	flags;				/* Add flag bits */
    putl (f, templong);
    i =	0;
    while (name[i])
	putc (name[i++], f);			/* Write a byte	*/
    while (i & 3) {
	putc ('\0', f);                         /* Pad the last word */
	i++;
    }
}



LongPut	(f, data, length) FILE *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, k;

    for	(i = length - 1; i >= 0; i--) {
	j = (data >> (i	* 8)) &	0x00FF;
	k = j >> 4;
	fprintf	(f, "%c", k>9 ? k-10+'A' : k+'0');
	k = j &	0x0F;
	fprintf	(f, "%c", k>9 ? k-10+'A' : k+'0');
    }
}



putl (f, data) FILE *f;	long data;
/* Writes to file "f" the binary contents of "data" */
{
    register int  i;
    register char byte;

    for	(i = 0;	i < 4; i++) {
	byte = (data >>	((3 - i) * 8)) & 0x00FF;
	putc (byte, f);
    }
}



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 struct InFCtl *inf;
    register int i;

    if (!Pass2)
	return;				/* Active during pass 2	only */

    if (errlim < ERRMAX) {		/* Save	error data */
	errcode[errlim]	= errornum;
	errpos[errlim] = pos;
	errlim++;
    }
    if (errlim == 1) {		/* First error for this	line? */
	printf ("    \n");                      /* Wipe out line counter */
	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); /* Line in error */
    }
    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	*/
}
