/*
	oxccb.c -- v1.430 architecture neutral format (anf) to bytecode generator

	Copyright (c) 1995
	Norman D. Culver dba
	Oxbow Software
	1323 S.E. 17th Street #662
	Ft. Lauderdale, FL 33316
	(954) 463-4754
	ndc@icanect.net
	All rights reserved.

 * Redistribution and use in source and binary forms are permitted
 * provided that: (1) source distributions retain this entire copyright
 * notice and comment, and (2) distributions including binaries display
 * the following acknowledgement:  ``This product includes software
 * developed by Norman D. Culver dba Oxbow Software''
 * in the documentation or other materials provided with the distribution
 * and in all advertising materials mentioning features or use of this
 * software.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

*/
#define MAJOR_VERSION 1
#define MINOR_VERSION 433

void oxcc_debug();
int __builtin_iv();
void bterpdebug(void);

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <setjmp.h>
#include <time.h>

#define SUPPORT_LONG_DOUBLE 1
#define SUPPORT_LONG_LONG 1

#define NEED_SPELLING 1
#define NEED_BYTECODES 1
#define NEED_AOUT_FORMAT 1
#include "oxbytes.h"

#define NEED_FUNCTHUNK 1
#define NEED_ANFDEFS 1
#include "oxanf.h"
	
#define PROG oxccb
#define USING_FRAMEWORK 1
#define HOST_IS_LITTLE_ENDIAN 1
#define REALLY_NEED_OFFSETS 1
#define FUNCDATA (iv->category+1)

#define VFPRINTF(a,b) vfprintf(stderr,a,b)
#define PERROR prerror
#define PWARN prwarn
#define PRINTF info
static void prerror(const char *, ...);
static void prwarn(const char *, ...);
static void info(const char *, ...);
int cfeprintf(const char *, ...);

#define FILEWRITE(buf, cnt)\
{if(!iv->errors){if(fwrite(buf, 1, cnt, iv->outfile) != cnt)iv->errors = 12;}}

#define ROUNDING(a,b) ((a+(b-1))&~(b-1))
#define ROUNDUP(a,b) a += ROUNDING(a,b)

#define KEYEQ(a,b) ((a)[0] == (b)[0] && (a)[1] == (b)[1])
#define KEYLT(a,b) (((a)[1] < (b)[1]) || ((a)[1] == (b)[1] && (a)[0] < (b)[0]))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))

/* ======================== CONCATENIZATION MACROS ==================== */

#define	_cat2_(a, b)	a##b
#define _cat_(a, b)	_cat2_(a, b)
#define Global(a) _cat_(PROG, a)

#define _pname2_(x)	#x
#define _pname1_(x)	_pname2_(x)
#define pName		_pname1_(PROG)


/* ============== ENDIAN MACROS (input format is litle endian) ==== */

#if HOST_IS_LITTLE_ENDIAN
#define GL(a) a
#define GS(a) a
#define PL(a) a
#define PS(a) a
#else
#endif

/* =================== INPUT DATA FORMATS ========================== */

#define INFILE_SYMNUM 1
#define OUTFILE_SYMNUM 2

static unsigned char binops[] = {0,0,
ADD,SUB,MUL,DIV,LSH,RSH,MOD,OR,XOR,AND,EQ,NE,LT,GT,LE,GE,
NEG,COMP,NOT
};
/* ====================== STRUCTURES AND TYPEDEFS ======================== */
typedef struct _jl {
	struct _jl *next;
	void *p;
	char *q;
	long *plabelval;
	long offset;
} *PJL;

typedef struct _el {
	struct _el *next;
	long spot;
	short symnum;
} *PEL;

typedef struct _afile {
	unsigned char *file_p;
	PopI header_p;
	PopI size_p;
	unsigned char *symtext_p;
	unsigned char *prog_p;
	unsigned char *data_p;
	unsigned char *switch_p;
	unsigned char *decl_p;
	unsigned char *maxtemp_p;
	unsigned char *seg_p;
	unsigned char **symaddr;
	unsigned char **decladdr;
	unsigned long thunk_offset;
	unsigned long bss_offset;
	int maxtemp;
	int maxtempclass;
	void *datatbl;
	short *symtran;
	unsigned short *decltran;
	int filenum;
	int numsyms;
	int numdecls;
	int numrelocs;
	int numsegs;
} *Pafile;

typedef struct _iv {
	int category;
	FILE *outfile;
	struct exec *header;
	unsigned char **symaddr;
	unsigned char **decladdr;
	int remove_infile;
	int argc;
	char **argv;
	int numfiles;
	int lastlabel;
	int errors;
	int numsyms;
	int numdecls;
	int numsegs;
	int maxtemp;
	int maxtempclass;
	unsigned long total_size;
	unsigned long thunk_offset;
	unsigned long bss_offset;

	struct _nodeO *ob_usedhead;
	struct _nodeO *ob_usedtail;
	struct _nodeO *ob;
	unsigned char *ob_buf;
	int ob_bufcnt;
	struct _nodeO *first_ob;

	struct _nodeC *cod_usedhead;
	struct _nodeC *cod_usedtail;
	struct _nodeC *cod;
	unsigned char *cod_buf;
	int cod_bufcnt;
	struct _nodeC *first_cod;
	struct _nodeC *regcode;

	long first_temp;
	long killop;
	long stackdepth;
	long maxdepth;
	long mindepth;
	long numnested;
	long lastline;
	void *reloctbl;
	void *extrntbl;
	void *gbltbl;
	void *symtbl;
	void *labeltbl;
	void *newlabeltbl;
	void *tmptbl;
	void *segtbl;
	void *functbl;
	void *finalsymtbl;
	void *finalstringpack;
	long finalpacksize;
	void *datatbl;
	void *builtintbl;
	int in_builtin;
	int has_structret;
	int temps_written;
	unsigned char *obuf;
	unsigned char *obufstart;
	PEL extbuf;
	void *extbufstart;
	int extcnt;
	int extbufcnt;
	PEL finextbuf;
	void *finextbufstart;
	int finextcnt;
	int finextbufcnt;
	PJL jbuf;
	void *jbufstart;
	int jmpcnt;
	int jbufcnt;
	long obufcnt;
	long out_offset;
	long func_offset;
	int extmark;
	short markedsym[10];
	char *markedbuf[10];
	int filenum;
	Pafile files[1024];
	char debug;
	char only_debug;
	char strip;
	char listing_wanted;
} *Piv;

struct _gloval {
	char *symname;
	int symnum;
	unsigned char *p;
	Pafile pf;
};
struct _rkey {/* key area of reloctbl node */
	unsigned long spot;
	short fileno;
	unsigned char opcode;
	char rsize;
};
struct _rval {/* value area of reloctbl node */
	unsigned char *p;
	unsigned long *base;
	long offset;
	short rsym;
};


/* Internal User API */
static void *Cmalloc(int category, unsigned amount);
static void *Ccalloc(int category, unsigned nelems, unsigned elemsize);
static void *Crealloc(int category, void* buf, unsigned newsize);
static void Cfree(int category, void* buf);
static void Cfreecat(int category);
static int Cmemrange(int category, unsigned* minp, unsigned* maxp);
static int Cusedrange(int category, unsigned* minp, unsigned* maxp);
static void Ctotrange(unsigned* minp,unsigned* maxp);
static int Cnewcat(void);
static void Cguard(int category);
static void* NewSymTable(int category, int nbins);
static int SymFind(void *tbl, void *key, void *result);
static int SymFindRange(void *tbl, void *key, void *result);
static void *SymInsert(void *tbl, void *key, void *value, int datsiz);
static int StringInsert(void *tbl, char *string, void *result);
static int StringFind(void *tbl, char *string, void *result);
static void SymDelete(void *tbl, void *key);
static int SymHead(void *tbl);
static int SymNext(void *tbl);
static void SymGetMark(void *tbl, void *markptr);
static int SymMarkNext(void *tbl, void *mark);
static void SymSetMark(void *tbl, void *markptr);
static void SymKey(void *tbl, void *keyptr);
static void SymValue(void *tbl, void *datptr);
static void *seg_find(Piv iv, int id);
static char *filenameof(char *path);
static char *propernameof(Piv iv, char *name);

/* END: User API */

/* ====================== PUT UNIQUE CODE HERE ========================= */
static void newlabel_insert(Piv iv, long label);
static long newlabel_fix(Piv iv, long label);
static void *do_stmt(Piv iv, unsigned char *p);
static void *do_expr(Piv iv, unsigned char *p);
static void do_bracket(Piv iv, unsigned char *p, unsigned char *q);
static void *do_something(Piv iv, unsigned char *p);
extern char *ctime();

/* ===================== BYTECODE OUTPUT GENERATOR ======================= */
struct _nodeOBUF
{
	struct _nodeOBUF *next;
	long cnt;
	char buf[1];
};
typedef struct _nodeC
{
	struct _nodeC *next;
	struct _nodeOBUF *ee;
} NODEC, *PNODEC;

typedef struct _nodeO
{
	struct _nodeO *next;
	unsigned char *p;
	ND d;
	ND l;
	ND r;
	PNODEC startinst;
	PNODEC endinst;
} NODEO, *PNODEO;

static unsigned char get_datasize(unsigned char, PND);
static void link_cod(Piv);

static char *notice = 
	"  Generated by Oxbow Software Bytecode Backend version %d.%d\n*/\n\n";

static struct _nd longtype = 		{D_SIGNED,0,B4,0,4,0,0};
static struct _nd longlongtype = 	{D_SIGNED,0,B8,0,8,0,0};
static char padit[8];	/* in bss */

void
bterpdebug(){}

static long
symnumof(Piv iv, char *symb)
{
struct _gloval *valp;

	if(StringFind(iv->gbltbl, symb, &valp))
		return (long)valp->pf->symtran[valp->symnum];
	return 0;
}
static void
buildin(Piv iv, char *symb, unsigned char code)
{
long key[2];

	if((key[0] = symnumof(iv, symb)))
	{
		key[1] = 0;
		SymInsert(iv->builtintbl, key, &code, 1);
	}
}
static void
install_builtins(Piv iv)
{/* USE THIS TO INSTALL WHATEVER BUILTINS ARE IN THE TARGET INTERPRETER */
#define BUILDIN(a,b) buildin(iv,#a,b)

	iv->builtintbl = NewSymTable(iv->category, 191);

	BUILDIN(alloca,ALLOCA);
	BUILDIN(strlen,STRLEN);
	BUILDIN(strcpy,STRCPY);
	BUILDIN(strcat,STRCAT);
	BUILDIN(memcpy,MEMCPY);
	BUILDIN(memmove,MEMMOVE);
	BUILDIN(bzero,BZERO);
	BUILDIN(malloc,MALLOC);
	BUILDIN(calloc,CALLOC);
	BUILDIN(realloc,REALLOC); 		
	BUILDIN(setjmp,SETJMP);
	BUILDIN(longjmp,LONGJMP);
	BUILDIN(abort,ABORT);
	BUILDIN(exit, EXIT);
	BUILDIN(_exit,EXIT);
	BUILDIN(bterpdebug,DEBUG);
	BUILDIN(bterpnodebug,NODEBUG);

#undef BUILDIN
}
static long
final_strofs(Piv iv, char *string)
{
long *result;

	if(StringFind(iv->finalsymtbl, string, &result))
		return result[2];	
	return 0;
}
static short
final_symnum(Piv iv, short symnum)
{
long *result;

	if(StringFind(iv->finalsymtbl, iv->symaddr[symnum], &result)) {
		return result[1]-1;
	}
	return 0;
}
static void
make_final_symtab(Piv iv)
{
int i;
	iv->finalsymtbl = NewSymTable(iv->category, 0);
	if(SymHead(iv->gbltbl))
	{
		i = 0;
		while(SymNext(iv->gbltbl))
		{
		long *result;
		struct _gloval *valp;

			SymValue(iv->gbltbl, &valp);
			if(*(valp->p))
			{
			long key[2];
				key[0] = valp->pf->symtran[valp->symnum];
				key[1] = 0;
				if(!SymFind(iv->builtintbl, key, NULL))
				{
				  if(!StringInsert(iv->finalsymtbl, valp->symname, &result))
				  {/* New Entry */
					result[1] = ++i;
				  }
				}
			}
		}
	}
}
static void
adjust_labels(Piv iv, long base, long adjust)
{
long *key;
long *val;
	if(SymHead(iv->newlabeltbl))
	{
		while(SymNext(iv->newlabeltbl))
		{
			SymKey(iv->newlabeltbl, &key);
			SymValue(iv->newlabeltbl, &val);
			if(key[1] == iv->filenum &&	val[0] > base) {
				val[0] += adjust;
			}
		}
	}
}
static void
addto_extlist(Piv iv, char *buf)
{
void *next;
	while(iv->extmark > 0)
	{
	long offset = iv->markedbuf[iv->extmark] - buf;
		next = iv->extbuf;
		if(iv->extbufcnt >= sizeof(struct _el))
		{
			iv->extbuf++;
		}
		else
		{
			iv->extbufcnt = 4080;
			iv->extbuf = Ccalloc(FUNCDATA, 1, iv->extbufcnt);
		}
		*((void**)next) = iv->extbuf;
		iv->extbuf->spot = iv->out_offset+iv->func_offset+offset;
		iv->extbuf->symnum = iv->markedsym[iv->extmark];
		++iv->extcnt;
		--iv->extmark;
		iv->extbufcnt -= sizeof(struct _el);
	}
}
static void
save_extlocs(Piv iv)
{
PEL pel = iv->extbufstart;
void *next;

	while(pel)
	{
		next = iv->finextbuf;
		if(iv->finextbufcnt >= sizeof(struct _el))
		{
			iv->finextbuf++;
		}
		else
		{
			iv->finextbufcnt = 4080;
			iv->finextbuf = Ccalloc(iv->category, 1, iv->finextbufcnt);
		}
		*((void**)next) = iv->finextbuf;
		iv->finextbuf->spot = pel->spot;
		iv->finextbuf->symnum = pel->symnum;
		++iv->finextcnt;
		iv->finextbufcnt -= sizeof(struct _el);
		pel = pel->next;
	}
}
static void
adjust_externs(Piv iv, long base, long adjust)
{
PEL pel = iv->extbufstart;

	while(pel)
	{
		if(pel->spot > base)
			pel->spot += adjust;
		pel = pel->next;
	}
}

static int
shorten_jmps(Piv iv)
{
int scnt;
PJL jp;
long jmp_offset;
long label_offset;
long diff;
long offset_adjust;
int sign;
unsigned char *q, *osiz;
int cursize, newsize;

	scnt = 0;
	jp = iv->jbufstart;
	offset_adjust = 0;
	while(jp)
	{
		osiz = jp->q+4;				/* points to size in obuf */
		q = osiz+4;					/* points to the JMP inst in obuf */
		cursize = *q & 3;			/* from the output bytecode */
		jmp_offset = jp->offset + offset_adjust;
		jp->offset = jmp_offset;	/* reset the offset for this inst */
		label_offset = *jp->plabelval;
		diff = label_offset - jmp_offset;

		sign = 1;
		if(diff < 0)
		{
			sign = -1;
			diff = -diff;
		}
		if(diff < 0x7fL)
			newsize = 0;
		else if(diff < 0x7fffL)
			newsize = 1;
		else if(diff < 0x007fffffL)
			newsize = 2;
		else
			newsize = 3;
		if(cursize != newsize)
		{/* DO SOMETHING */
		long adj;
		static long codesize[4] = {2L,3L,4L,5L};
			++scnt;								/* something changed */
			*q &= 0xfc;						/* mask opcode */
			*q |= newsize;					/* set size in opcode */
			*((long*)osiz) = codesize[newsize];	/* set new output size */
			adj = codesize[newsize] - codesize[cursize];
			offset_adjust += adj;	/* jmps below this point have new offset */
			if(sign > 0) diff += adj;	/* label is below this point */

			/* Adjust all labels below this point */
			adjust_labels(iv, jmp_offset, adj);
			/* adjust all external address spots (text relocs) below this point */
			adjust_externs(iv, jmp_offset, adj);
		}
		diff *= sign;	/* restore sign to the relative address */

		/* generate the pc relative address */

		++q;	/* points the the address field of the JMP inst */
		if(newsize == 0)
			*((signed char*)q) = (signed char)(diff & 0xff);
		else if(newsize == 1)
			*((short*)q) = (short)(diff & 0xffff);
		else if(newsize == 2)
			*((long*)q) = diff<<8;
		else
			*((long*)q) = diff;
		jp = jp->next;
	}/* END: while (jp) */

	return scnt;
}

static void
setup_jmps(Piv iv, unsigned char *pdef)
{
struct {
	long k1;
	long k2;
} key;	
long *result;
PJL jp;
unsigned char *p;

	jp = iv->jbufstart;
	while(jp)
	{
		p = jp->p;
		key.k1 = GL( POP->data );	/* label number from source code */
		key.k2 = iv->filenum;			/* source file number */
		if(SymFind(iv->newlabeltbl, &key, &result))
		{
			jp->plabelval = result;	/* save pointer to label value */
		}
		else
		{
		PERROR(pName ":SYSERROR: jmp setup failed for `%s' label=%d file=%d p=%x code=%d\n",
		iv->symaddr[GL(((Pop)pdef)->data)], key.k1, key.k2, p, *p);
		}
		jp = jp->next;
	}
}
static void
addto_jmplist(Piv iv, unsigned char *p, void *q)
{
void *next = iv->jbuf;

	if(iv->jbufcnt >= sizeof(struct _jl))
	{
		iv->jbuf++;
	}
	else
	{
		iv->jbufcnt = 4080;
		iv->jbuf = Ccalloc(FUNCDATA, 1, iv->jbufcnt);
	}
	*((void**)next) = iv->jbuf;
	iv->jbuf->p = p;
	iv->jbuf->q = q;
	iv->jbuf->offset = iv->out_offset+iv->func_offset-5;
	iv->jbufcnt -= sizeof(struct _jl);
}
static void
save_maxdepth(Piv iv, long symnum)
{
long key[2];
long val[3];

	if(iv->functbl == 0)
	{
		iv->functbl = NewSymTable(iv->category, 277);
	}
	key[0] = symnum;
	key[1] = 0;
	val[0] = iv->maxdepth + (4*iv->numnested);
	val[1] = iv->mindepth;	
	val[2] = 0;
	SymInsert(iv->functbl, key, val, 12);
}	
static long
get_maxdepth(Piv iv, short symnum)
{
long key[2];
long *result;

	key[0] = symnum;
	key[1] = 0;
	if(SymFind(iv->functbl, key, &result))
	{
		return result[0];
	}
	return 0;
}
static void
printline(Piv iv, void *ptr)
{
	fprintf(iv->outfile, "Line:%ld:\n", *((long*)ptr));
}
static int
print8(Piv iv, unsigned char *ptr, int size, long offset, int lf)
{
char buf[40];
int i, j=0;

	i = sprintf(buf, "%8.8lx: ", offset);
	while(j < size && j < 8)
	{
		i += sprintf(&buf[i], "%2.2x ", *ptr++);
		++j;
	}
	if(lf)
		buf[i++] = '\n';
	else
	{
		while(i < 35)
			buf[i++] = ' ';
	}
	buf[i] = 0;
	fprintf(iv->outfile, buf);
	return j;
}
static void
printinst(Piv iv, unsigned char *pc, long size)
{
int i, bufcnt;
unsigned char *epc;
char *pbuf[20];

  bufcnt = 0;
  epc = pc + size;
  for( ;pc < epc; ++pc)
  {
	switch(*pc)
	{
		case LOCATE|J1:
		case LOCATE|J2:
		case LOCATE|J3:
		case LOCATE|J4:
			pbuf[bufcnt++] = "locate|";
			pbuf[bufcnt++] = Jbuf[*pc & 0x03];
			pc += Jcnt[*pc & 0x03];
			break;
		case LS|A1|B1:
		case LS|A1|B2:
		case LS|A1|B4:
		case LS|A1|B8:
		case LS|A2|B1:
		case LS|A2|B2:
		case LS|A2|B4:
		case LS|A2|B8:
		case LS|A3|B1:
		case LS|A3|B2:
		case LS|A3|B4:
		case LS|A3|B8:
			pbuf[bufcnt++] = "ls|";
			pbuf[bufcnt++] = ABbuf[*pc & 0x0f];
			pc += Acnt[(*pc>>2) & 0x03];
			break;
		case NEG|BYTE:
		case NEG|SHORT:
		case NEG|LONG:
		case NEG|UBYTE:
		case NEG|USHORT:
		case NEG|ULONG:
		case NEG|FLOAT:
		case NEG|DOUBLE:
			pbuf[bufcnt++] = "neg|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case LM|A1|B1:
		case LM|A1|B2:
		case LM|A1|B4:
		case LM|A1|B8:
		case LM|A2|B1:
		case LM|A2|B2:
		case LM|A2|B4:
		case LM|A2|B8:
		case LM|A3|B1:
		case LM|A3|B2:
		case LM|A3|B4:
		case LM|A3|B8:
		case LM|A4|B1:
		case LM|A4|B2:
		case LM|A4|B4:
		case LM|A4|B8:
			pbuf[bufcnt++] = "lm|";
			pbuf[bufcnt++] = ABbuf[*pc & 0x0f];
			pc += Acnt[(*pc>>2) & 0x03];
			break;

		case COMP|B1:
		case COMP|B2:
		case COMP|B4:
		case COMP|B8:
			pbuf[bufcnt++] = "comp|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			break;

		case JMP|J1:
		case JMP|J2:
		case JMP|J3:
		case JMP|J4:
			pbuf[bufcnt++] = "jmp|";
			pbuf[bufcnt++] = Jbuf[*pc & 0x03];
			pc += Jcnt[*pc & 0x03];
			break;

		case JMPT|J1:
		case JMPT|J2:
		case JMPT|J3:
		case JMPT|J4:
			pbuf[bufcnt++] = "jmpt|";
			pbuf[bufcnt++] = Jbuf[*pc & 0x03];
			pc += Jcnt[*pc & 0x03];
			break;

		case LJMPT|J1:
		case LJMPT|J2:
		case LJMPT|J3:
		case LJMPT|J4:
			pbuf[bufcnt++] = "ljmpt|";
			pbuf[bufcnt++] = Jbuf[*pc & 0x03];
			pc += Jcnt[*pc & 0x03];
			break;

		case JMPF|J1:
		case JMPF|J2:
		case JMPF|J3:
		case JMPF|J4:
			pbuf[bufcnt++] = "jmpf|";
			pbuf[bufcnt++] = Jbuf[*pc & 0x03];
			pc += Jcnt[*pc & 0x03];
			break;

		case LJMPF|J1:
		case LJMPF|J2:
		case LJMPF|J3:
		case LJMPF|J4:
			pbuf[bufcnt++] = "ljmpf|";
			pbuf[bufcnt++] = Jbuf[*pc & 0x03];
			pc += Jcnt[*pc & 0x03];
			break;

		case NOT|B1:
		case NOT|B2:
		case NOT|B4:	/* also FLOAT */
		case NOT|B8:	/* also DOUBLE */
			pbuf[bufcnt++] = "not|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			break;

		case SS|A1|B1:
		case SS|A1|B2:
		case SS|A1|B4:
		case SS|A1|B8:
		case SS|A2|B1:
		case SS|A2|B2:
		case SS|A2|B4:
		case SS|A2|B8:
		case SS|A3|B1:
		case SS|A3|B2:
		case SS|A3|B4:
		case SS|A3|B8:
			pbuf[bufcnt++] = "ss|";
			pbuf[bufcnt++] = ABbuf[*pc & 0x0f];
			pc += Acnt[(*pc>>2) & 0x03];
			break;

		case TRUTHOF|B2:
		case TRUTHOF|B4:
		case TRUTHOF|B8:
			pbuf[bufcnt++] = "truthof|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			break;
		case CVT:
		{
		unsigned char cvs,cvd;
			pbuf[bufcnt++] = "cvt ";
			++pc;
			cvs = (*pc & 0x70)>>4;
			cvd = (*pc & 0x07);
			pbuf[bufcnt++] = Tbuf[cvd];
			pbuf[bufcnt++] = "<-";
			pbuf[bufcnt++] = Tbuf[cvs];
			break;
		}
		case LI|B1:
		case LI|B2:
		case LI|B4:
		case LI|B8:
			pbuf[bufcnt++] = "li|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			pc += Bcnt[*pc & 0x03];
			break;

		case LAI|D1:
		case LAI|D2:
		case LAI|D3:
		case LAI|D4:
			pbuf[bufcnt++] = "lai|";
			pbuf[bufcnt++] = Dbuf[*pc & 0x03];
			pc += Dcnt[*pc & 0x03];
			break;

		case LUI|B1:
		case LUI|B2:
		case LUI|B4:
		case LUI|B8:
			pbuf[bufcnt++] = "lui|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			pc += Bcnt[*pc & 0x03];
			break;

		case IMMED:
		{
			++pc;
			switch(*pc)
			{
				case SMI|A1|B1:
				case SMI|A1|B2:
				case SMI|A1|B4:
				case SMI|A1|B8:
				case SMI|A2|B1:
				case SMI|A2|B2:
				case SMI|A2|B4:
				case SMI|A2|B8:
				case SMI|A3|B1:
				case SMI|A3|B2:
				case SMI|A3|B4:
				case SMI|A3|B8:
				case SMI|A4|B1:
				case SMI|A4|B2:
				case SMI|A4|B4:
				case SMI|A4|B8:
					pbuf[bufcnt++] = "smi|";
					pbuf[bufcnt++] = ABbuf[*pc & 0x0f];
					pc += ABcnt[*pc & 0x0f];
					break;

				case SSI|A1|B1:
				case SSI|A1|B2:
				case SSI|A1|B4:
				case SSI|A1|B8:
				case SSI|A2|B1:
				case SSI|A2|B2:
				case SSI|A2|B4:
				case SSI|A2|B8:
				case SSI|A3|B1:
				case SSI|A3|B2:
				case SSI|A3|B4:
				case SSI|A3|B8:
					pbuf[bufcnt++] = "ssi|";
					pbuf[bufcnt++] = ABbuf[*pc & 0x0f];
					pc += ABcnt[*pc & 0x0f];
					break;

				case MODI|BYTE:
				case MODI|SHORT:
				case MODI|LONG:
				case MODI|UBYTE:
				case MODI|USHORT:
				case MODI|ULONG:
					pbuf[bufcnt++] = "modi|";
					pbuf[bufcnt++] = Tbuf[*pc & 0x07];
					pc += 2;
					break;

				case DEREF|BYTE:
				case DEREF|SHORT:
				case DEREF|LONG:
				case DEREF|UBYTE:
				case DEREF|USHORT:
				case DEREF|ULONG:
				case DEREF|FLOAT:
				case DEREF|DOUBLE:
					pbuf[bufcnt++] = "deref|";
					pbuf[bufcnt++] = Tbuf[*pc & 0x07];
					break;

				case DEREF1|BYTE:
				case DEREF1|SHORT:
				case DEREF1|LONG:
				case DEREF1|UBYTE:
				case DEREF1|USHORT:
				case DEREF1|ULONG:
				case DEREF1|FLOAT:
				case DEREF1|DOUBLE:
					pbuf[bufcnt++] = "deref1|";
					pbuf[bufcnt++] = Tbuf[*pc & 0x07];
					break;
			}
			break;
		}
		case SM|A1|B1:
		case SM|A1|B2:
		case SM|A1|B4:
		case SM|A1|B8:
		case SM|A2|B1:
		case SM|A2|B2:
		case SM|A2|B4:
		case SM|A2|B8:
		case SM|A3|B1:
		case SM|A3|B2:
		case SM|A3|B4:
		case SM|A3|B8:
		case SM|A4|B1:
		case SM|A4|B2:
		case SM|A4|B4:
		case SM|A4|B8:
			pbuf[bufcnt++] = "sm|";
			pbuf[bufcnt++] = ABbuf[*pc & 0x0f];
			pc += Acnt[(*pc>>2) & 3];
			break;

		case ADD|BYTE:
		case ADD|SHORT:
		case ADD|LONG:
		case ADD|UBYTE:
		case ADD|USHORT:
		case ADD|ULONG:
		case ADD|FLOAT:
		case ADD|DOUBLE:
			pbuf[bufcnt++] = "add|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case SUB|BYTE:
		case SUB|SHORT:
		case SUB|LONG:
		case SUB|UBYTE:
		case SUB|USHORT:
		case SUB|ULONG:
		case SUB|FLOAT:
		case SUB|DOUBLE:
			pbuf[bufcnt++] = "sub|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case MUL|BYTE:
		case MUL|SHORT:
		case MUL|LONG:
		case MUL|UBYTE:
		case MUL|USHORT:
		case MUL|ULONG:
		case MUL|FLOAT:
		case MUL|DOUBLE:
			pbuf[bufcnt++] = "mul|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case DIV|BYTE:
		case DIV|SHORT:
		case DIV|LONG:
		case DIV|UBYTE:
		case DIV|USHORT:
		case DIV|ULONG:
		case DIV|FLOAT:
		case DIV|DOUBLE:
			pbuf[bufcnt++] = "div|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case OR|B1:
		case OR|B2:
		case OR|B4:
		case OR|B8:
			pbuf[bufcnt++] = "or|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			break;

		case AND|B1:
		case AND|B2:
		case AND|B4:
		case AND|B8:
			pbuf[bufcnt++] = "and|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			break;

		case MOD|BYTE:
		case MOD|SHORT:
		case MOD|LONG:
		case MOD|UBYTE:
		case MOD|USHORT:
		case MOD|ULONG:
			pbuf[bufcnt++] = "mod|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case XTD:
		{
			++pc;
			switch(*pc)
			{
				case LI:
					pbuf[bufcnt++] = "li";
					pc += XSZ;
					break;

				case LSH|B1:
				case LSH|B2:
				case LSH|B4:
				case LSH|B8:
					pbuf[bufcnt++] = "lsh|";
					pbuf[bufcnt++] = Bbuf[*pc & 0x03];
					break;

				case LSHI|B1:
				case LSHI|B2:
				case LSHI|B4:
				case LSHI|B8:
					pbuf[bufcnt++] = "lshi|";
					pbuf[bufcnt++] = Bbuf[*pc & 0x03];
					++pc;
					break;

				case RSH|BYTE:
				case RSH|SHORT:
				case RSH|LONG:
				case RSH|UBYTE:
				case RSH|USHORT:
				case RSH|ULONG:
					pbuf[bufcnt++] = "rsh|";
					pbuf[bufcnt++] = Tbuf[*pc & 0x07];
					break;

				case RSHI|BYTE:
				case RSHI|SHORT:
				case RSHI|LONG:
				case RSHI|UBYTE:
				case RSHI|USHORT:
				case RSHI|ULONG:
					pbuf[bufcnt++] = "rshi|";
					pbuf[bufcnt++] = Tbuf[*pc & 0x07];
					++pc;
					break;

				case BUILTIN:
				{
					pbuf[bufcnt++] = "builtin ";
					++pc;
					pbuf[bufcnt++] = BUbuf[*pc];
					break;
				} /* END: XTD BUILTIN */
				case CLRDAT:
					pbuf[bufcnt++] = "clrdat";
					break;
				case SWITCH:
					pbuf[bufcnt++] = "switch";
					pc += 2;
					break;
				case CALLSETUP:
					pbuf[bufcnt++] = "callsetup";
					pc += 4;
					break;
				case RETSTRUCT:
					pbuf[bufcnt++] = "retstruct";
					pc += 4;
					break;
				case PRUNESTRUCT:
					pbuf[bufcnt++] = "prunestruct";
					break;
				case GETBITFIELD:
					pbuf[bufcnt++] = "getbitfield";
					pc += 3;
					break;
				case PUTBITFIELD:
					pbuf[bufcnt++] = "putbitfield";
					pc += 3;
					break;
				case IMMED:
				{
					++pc;
					switch(*pc)
					{
						case SMI|A1:
						case SMI|A2:
						case SMI|A3:
						case SMI|A4:
							pbuf[bufcnt++] = "smi|";
							pbuf[bufcnt++] = Abuf[(*pc>>2) & 0x03];
							pc += Acnt[(*pc>>2) & 0x03] + XSZ;
							break;

						case SSI|A1:
						case SSI|A2:
						case SSI|A3:
							pbuf[bufcnt++] = "ssi|";
							pbuf[bufcnt++] = Abuf[(*pc>>2) & 0x03];
							pc += Acnt[(*pc>>2) & 0x03] + XSZ;
							break;

						case MODI|LONGLONG:
						case MODI|ULONGLONG:
							pbuf[bufcnt++] = "modi|";
							pbuf[bufcnt++] = XTbuf[*pc & 0x03];
							pc += 2;
							break;

						case DEREF|LONGLONG:
						case DEREF|ULONGLONG:
						case DEREF|LONGDOUBLE:
							pbuf[bufcnt++] = "deref|";
							pbuf[bufcnt++] = XTbuf[*pc & 0x03];
							break;

						case DEREF1|LONGLONG:
						case DEREF1|ULONGLONG:
						case DEREF1|LONGDOUBLE:
							pbuf[bufcnt++] = "deref1|";
							pbuf[bufcnt++] = XTbuf[*pc & 0x03];
							break;
					}
					break;
				}/* END: XTD IMMED */
				
				case ADD|LONGLONG:
				case ADD|ULONGLONG:
				case ADD|LONGDOUBLE:
					pbuf[bufcnt++] = "add|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case SUB|LONGLONG:
				case SUB|ULONGLONG:
				case SUB|LONGDOUBLE:
					pbuf[bufcnt++] = "sub|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
				case MUL|LONGLONG:
				case MUL|ULONGLONG:
				case MUL|LONGDOUBLE:
					pbuf[bufcnt++] = "mul|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case DIV|LONGLONG:
				case DIV|ULONGLONG:
				case DIV|LONGDOUBLE:
					pbuf[bufcnt++] = "div|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case LT|LONGLONG:
				case LT|ULONGLONG:
				case LT|LONGDOUBLE:
					pbuf[bufcnt++] = "lt|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case GT|LONGLONG:
 				case GT|ULONGLONG:
				case GT|LONGDOUBLE:
					pbuf[bufcnt++] = "gt|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case LE|LONGLONG:
				case LE|ULONGLONG:
				case LE|LONGDOUBLE:
					pbuf[bufcnt++] = "le|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case GE|LONGLONG:
				case GE|ULONGLONG:
				case GE|LONGDOUBLE:
					pbuf[bufcnt++] = "ge|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case NE|LONGLONG:
				case NE|ULONGLONG:
				case NE|LONGDOUBLE:
					pbuf[bufcnt++] = "ne|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case EQ|LONGLONG:
				case EQ|ULONGLONG:
				case EQ|LONGDOUBLE:
					pbuf[bufcnt++] = "eq|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case NEG|LONGLONG:
				case NEG|ULONGLONG:
				case NEG|LONGDOUBLE:
					pbuf[bufcnt++] = "neg|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case NOT|BX:
					pbuf[bufcnt++] = "not|BX";
					break;
				case COMP|LONGLONG:
				case COMP|ULONGLONG:
					pbuf[bufcnt++] = "comp|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case RSH|SLONGLONG:
				case RSH|SULONGLONG:
					pbuf[bufcnt++] = "rsh|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;

				case MOD|LONGLONG:
				case MOD|ULONGLONG:
					pbuf[bufcnt++] = "mod|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x03];
					break;
				case RSHI|SLONGLONG:
				case RSHI|SULONGLONG:
					pbuf[bufcnt++] = "rshi|";
					pbuf[bufcnt++] = XTbuf[*pc & 0x07];
					++pc;
					break;
				case TRUTHOF|BX:
					pbuf[bufcnt++] = "truthof|BX";
					break;

				case LS|A1:
				case LS|A2:
				case LS|A3:
					pbuf[bufcnt++] = "xtd ls|";
					pbuf[bufcnt++] = Abuf[(*pc>>2) & 0x03];
					pc += Acnt[(*pc>>2) & 0x03];
					break;

				case LM|A1:
				case LM|A2:
				case LM|A3:
				case LM|A4:
					pbuf[bufcnt++] = "xtd lm|";
					pbuf[bufcnt++] = Abuf[(*pc>>2) & 0x03];
					pc += Acnt[(*pc>>2) & 0x03];
					break;

				case SS|A1:
				case SS|A2:
				case SS|A3:
					pbuf[bufcnt++] = "xtd ss|";
					pbuf[bufcnt++] = Abuf[(*pc>>2) & 0x03];
					pc += Acnt[(*pc>>2) & 0x03];
					break;

				case SM|A1:
				case SM|A2:
				case SM|A3:
				case SM|A4:
					pbuf[bufcnt++] = "xtd sm|";
					pbuf[bufcnt++] = Abuf[(*pc>>2) & 0x03];
					pc += Acnt[(*pc>>2) & 0x03];
					break;

				case MOVSS:
					pbuf[bufcnt++] = "xtd movss";
					++pc;
					pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
					pc += SDcnt[*pc & 0x0f];
					break;
				case MOVSM:
					pbuf[bufcnt++] = "xtd movsm";
					++pc;
					pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
					pc += SDcnt[*pc & 0x0f];
					break;
				case MOVMS:
					pbuf[bufcnt++] = "xtd movms";
					++pc;
					pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
					pc += SDcnt[*pc & 0x0f];
					break;
				case MOVMM:
					pbuf[bufcnt++] = "xtd movmm";
					++pc;
					pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
					pc += SDcnt[*pc & 0x0f];
					break;
			}
			break;
		}/* END: XTD */

		case XOR|B1:
		case XOR|B2:
		case XOR|B4:
		case XOR|B8:
			pbuf[bufcnt++] = "xor|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			break;

		case GT|BYTE:
		case GT|SHORT:
		case GT|LONG:
		case GT|UBYTE:
		case GT|USHORT:
		case GT|ULONG:
		case GT|FLOAT:
		case GT|DOUBLE:
			pbuf[bufcnt++] = "gt|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case LT|BYTE:
		case LT|SHORT:
		case LT|LONG:
		case LT|UBYTE:
		case LT|USHORT:
		case LT|ULONG:
		case LT|FLOAT:
		case LT|DOUBLE:
			pbuf[bufcnt++] = "lt|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case GE|BYTE:
		case GE|SHORT:
		case GE|LONG:
		case GE|UBYTE:
		case GE|USHORT:
		case GE|ULONG:
		case GE|FLOAT:
		case GE|DOUBLE:
			pbuf[bufcnt++] = "ge|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case LE|BYTE:
		case LE|SHORT:
		case LE|LONG:
		case LE|UBYTE:
		case LE|USHORT:
		case LE|ULONG:
		case LE|FLOAT:
		case LE|DOUBLE:
			pbuf[bufcnt++] = "le|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case NE|BYTE:
		case NE|SHORT:
		case NE|LONG:
		case NE|UBYTE:
		case NE|USHORT:
		case NE|ULONG:
		case NE|FLOAT:
		case NE|DOUBLE:
			pbuf[bufcnt++] = "ne|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case EQ|BYTE:
		case EQ|SHORT:
		case EQ|LONG:
		case EQ|UBYTE:
		case EQ|USHORT:
		case EQ|ULONG:
		case EQ|FLOAT:
		case EQ|DOUBLE:
			pbuf[bufcnt++] = "eq|";
			pbuf[bufcnt++] = Tbuf[*pc & 0x07];
			break;

		case ARG:
			pbuf[bufcnt++] = "arg";
			break;
		case ARGA:
			pbuf[bufcnt++] = "arga";
			break;
		case ARGF:
			pbuf[bufcnt++] = "argf";
			break;

		case MOVSS|B1:
		case MOVSS|B2:
		case MOVSS|B4:
		case MOVSS|B8:
			pbuf[bufcnt++] = "movss|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			++pc;
			pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
			pc += SDcnt[*pc & 0x0f];
			break;

		case MOVSM|B1:
		case MOVSM|B2:
		case MOVSM|B4:
		case MOVSM|B8:
			pbuf[bufcnt++] = "movsm|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			++pc;
			pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
			pc += SDcnt[*pc & 0x0f];
			break;

		case MOVMS|B1:
		case MOVMS|B2:
		case MOVMS|B4:
		case MOVMS|B8:
			pbuf[bufcnt++] = "movms|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			++pc;
			pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
			pc += SDcnt[*pc & 0x0f];
			break;

		case MOVMM|B1:
		case MOVMM|B2:
		case MOVMM|B4:
		case MOVMM|B8:
			pbuf[bufcnt++] = "movmm|";
			pbuf[bufcnt++] = Bbuf[*pc & 0x03];
			++pc;
			pbuf[bufcnt++] = SDbuf[*pc & 0x0f];
			pc += SDcnt[*pc & 0x0f];
			break;

		case DUMP:
			pbuf[bufcnt++] = "dump ";
			break;
		case REGAIN:
			pbuf[bufcnt++] = "regain ";
			break;
		case CALL:
			pbuf[bufcnt++] = "call ";
			break;
		case RET:
			pbuf[bufcnt++] = "ret ";
			break;
		case SWAP:
			pbuf[bufcnt++] = "swap ";
			break;
		case SWAP4:
			pbuf[bufcnt++] = "swap4 ";
			break;
		case SWAP4DEEP:
			pbuf[bufcnt++] = "swap4deep ";
			break;
		case DUP:
			pbuf[bufcnt++] = "dup ";
			break;
		case DUP4:
			pbuf[bufcnt++] = "dup4 ";
			break;
		case ABSMEM:
			pbuf[bufcnt++] = "absmem ";
			break;
		case ABSSTK:
			pbuf[bufcnt++] = "absstk ";
			break;
		case MOVDA1:
			pbuf[bufcnt++] = "movda1 ";
			break;
		case MOVDA2:
			pbuf[bufcnt++] = "movda2 ";
			break;
		case MOVDA4:
			pbuf[bufcnt++] = "movda4 ";
			break;
		case MOVDA8:
			pbuf[bufcnt++] = "movda8 ";
			break;
		case MOVDAX:
			pbuf[bufcnt++] = "movdax ";
			break;
		case MOVAA1:
			pbuf[bufcnt++] = "movaa1 ";
			break;
		case MOVAA2:
			pbuf[bufcnt++] = "movaa2 ";
			break;
		case MOVAA4:
			pbuf[bufcnt++] = "movaa4 ";
			break;
		case MOVAA8:
			pbuf[bufcnt++] = "movaa8 ";
			break;
		case MOVAAX:
			pbuf[bufcnt++] = "movaax ";
			break;
		case MOVAAC:
			pbuf[bufcnt++] = "movaac ";
			break;
		case NOP:
			pbuf[bufcnt++] = "nop ";
			break;
	} /* END: switch(*pc) */
  }/* END: for() */
  for(i = 0; i < bufcnt; ++i)
		fwrite(pbuf[i], 1, strlen(pbuf[i]), iv->outfile);
  fwrite("\n", 1, 1, iv->outfile);
}/* END: printinst() */
static void
disassemble(Piv iv, void *ptr, long size)
{
int x;
long offset = iv->out_offset;

	x = print8(iv, ptr, size, offset, 0);
	printinst(iv, ptr, size);
	while((size -= x) > 0)
	{
		offset += x;
		((char*)ptr) += x;
		x = print8(iv, ptr, size, offset, 1);
	}
}
static void
reset_funcdata(Piv iv)
{
	iv->obuf = (char*)&iv->obufstart;
	iv->obufstart = 0;
	iv->obufcnt = 0;
	iv->func_offset = 0;
	iv->jbuf = (PJL)&iv->jbufstart;
	iv->jmpcnt = 0;
	iv->jbufcnt = 0;
	iv->jbufstart = 0;
	iv->extbuf = (PEL)&iv->extbufstart;
	iv->extcnt = 0;
	iv->extbufcnt = 0;
	iv->extbufstart = 0;
	iv->stackdepth = 0;
	iv->maxdepth = 0;
	iv->mindepth = 0;
	iv->numnested = 0;
	iv->cod_bufcnt = 0;
	iv->ob_bufcnt = 0;
	iv->cod_usedhead = 0;
	iv->ob_usedhead = 0;
	iv->first_cod = 0;
	iv->first_ob = 0;
	Cfreecat(FUNCDATA);
}
static void
write_funcdata(Piv iv, unsigned char *pdef)
{
long *p;
	if((p = (long*)iv->obufstart))
	{
		setup_jmps(iv, pdef);
		while(shorten_jmps(iv))
			;
		while(p)
		{
			if(iv->listing_wanted)
			{
				if(((unsigned char*)p)[8] == LINENO)
				{
					printline(iv, &((unsigned char*)p)[9]);
				}
				else if(((unsigned char*)p)[8] == NFUNC)
				{
				char *funcname = *((char**)&(((char*)p)[9]));
					fprintf(iv->outfile, "\n%8.8lx:    .nested .function _%s\n",
					iv->out_offset, funcname);
				}
				else
				{
					disassemble(iv, &p[2], p[1]);
					iv->out_offset += p[1];
				}
			}
			else
			{
				FILEWRITE(&p[2],p[1]);
				iv->out_offset += p[1];
			}
			p = (void*)p[0];
		}
	}
if(iv->debug >= '1')
cfeprintf("MAXDEPTH=%d MINDEPTH=%d func=%s\n",
iv->maxdepth, iv->mindepth, iv->symaddr[GL( ((Pop)pdef)->data )]);

	save_maxdepth(iv, GL( ((Pop)pdef)->data ));
}
static void *
write_obuf(Piv iv, unsigned char *buf, long cnt)
{/* Output first goes to a linked list */
void *next = iv->obuf;

	if(iv->obuf != (unsigned char*)&iv->obufstart)
	{/* Suppress duplicate RETs */
	  if(buf[0] == RET && (iv->obuf[8] == RET || iv->obuf[9] == RETSTRUCT) )
	  {
		return iv->obuf;
	  }
	}
	if(iv->obufcnt >= cnt+8)
	{/* There is sufficient room in the current chunk */
	long size = ((long*)iv->obuf)[1];
	  iv->obuf += size+8;
	}
	else
	{/* Allocate a new chunk of linked list space */
	  iv->obufcnt = 4080;
	  iv->obuf = Ccalloc(FUNCDATA, 1, iv->obufcnt);
	}

	*((void**)next) = iv->obuf;		/* link back to old entry */
	((long*)iv->obuf)[1] = cnt;		/* record the size of the data */
	iv->obufcnt -= cnt+8;			/* deduct data size plus overhead */
	memcpy(&((long*)iv->obuf)[2], buf, cnt);  /* copy the data to this area */
	if(buf[0] != LINENO && buf[0] != NFUNC)
	{/* A line number record is an anomoly */
		if(iv->extmark)
			addto_extlist(iv, buf);		/* reference to external variable here */
		iv->func_offset += cnt;			/* increase the program counter */
	}

	iv->cod->ee = (struct _nodeOBUF *)iv->obuf;	/* points to obuf chunks */
	link_cod(iv);

	return iv->obuf;	/* return the address of this sub chunk */
}
static void
do_conversion(Piv iv, PND dst, PND src)
{
unsigned char obuf[2];
int dsize = dst->size;
int ssize = src->size;
unsigned char ddtype = dst->dtype;
unsigned char sdtype = src->dtype;
#define SRC(a) (a<<4)

	if(src->atype & (A_ABSOLUTE|A_POINTER|A_VALUE) == (A_POINTER|A_VALUE))
	{
		if(src->atype & A_DATA)
			obuf[0] = ABSMEM;
		else if(src->atype & (A_AUTO|A_PARAM))
			obuf[0] = ABSSTK;
		else
			PERROR(pName ":ERROR: Line:%d bad conversion0\n", iv->lastline);
		write_obuf(iv, obuf, 1);
		return;
	}
	obuf[0] = CVT;
	switch(ddtype)
	{
		case	D_SIGNED:			/* dest is signed integer */
		{
			if(dsize == 1)
				obuf[1] = BYTE;
			else if(dsize == 2)
				obuf[1] = SHORT;
			else if(dsize == 4)
				obuf[1] = LONG;
			else if(dsize == 8)
				obuf[1] = CLONGLONG;
			switch(sdtype)
			{
				case D_SIGNED:
				{/* src is signed integer */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(BYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(SHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(LONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CLONGLONG);
					break;
				}
				case D_UNSIGNED:
				case D_SEGMENT:
				{/* src is unsigned integer */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				case D_FLOAT:
				{/* src is floating point */
					if(ssize == 4)
						obuf[1] |= SRC(FLOAT);
					else if(ssize == 8)
						obuf[1] |= SRC(DOUBLE);
					else
						obuf[1] |= SRC(CLONGDOUBLE);
					break;
				}
				case D_POINTER:
				case D_FUNCPTR:
				{/* src is unsigned int */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				default:
					PERROR(pName ":ERROR: Line:%d bad conversion1\n", iv->lastline);
			}
			break;
		}
		case	D_UNSIGNED:	/* dest is unsigned integer */
		case	D_SEGMENT:
		{
			if(dsize == 1)
				obuf[1] = UBYTE;
			else if(dsize == 2)
				obuf[1] = USHORT;
			else if(dsize == 4)
				obuf[1] = ULONG;
			else if(dsize == 8)
				obuf[1] = CULONGLONG;
			switch(sdtype)
			{
				case D_SIGNED:
				{/* src is signed integer */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(BYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(SHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(LONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CLONGLONG);
					break;
				}
				case D_UNSIGNED:
				case D_SEGMENT:
				{/* src is unsigned integer */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				case D_FLOAT:
				{/* src is floating point */
					if(ssize == 4)
						obuf[1] |= SRC(FLOAT);
					else if(ssize == 8)
						obuf[1] |= SRC(DOUBLE);
					else
						obuf[1] |= SRC(CLONGDOUBLE);
					break;
				}
				case D_POINTER:
				case D_FUNCPTR:
				{/* src is unsigned integer */
					if(dsize <= ssize)
						return;					
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				default:
					PERROR(pName ":ERROR: Line:%d bad conversion2\n", iv->lastline);
			}
			break;
		}
		case	D_FLOAT:	/* dest is float, double, long double */
		{
			if(dsize == 4)
				obuf[1] = FLOAT;
			else if(dsize == 8)
				obuf[1] = DOUBLE;
			else
				obuf[1] = CLONGDOUBLE;
			switch(sdtype)
			{
				case D_SIGNED:
				{/* src is signed integer */
					if(ssize == 1)
						obuf[1] |= SRC(BYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(SHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(LONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CLONGLONG);
					break;
				}
				case D_UNSIGNED:
				case D_SEGMENT:
				{/* src is unsigned integer */
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				case D_FLOAT:
				{/* src is floating point */
					if(dsize == ssize)
						return;
					if(ssize == 4)
						obuf[1] |= SRC(FLOAT);
					else if(ssize == 8)
						obuf[1] |= SRC(DOUBLE);
					else
						obuf[1] |= SRC(CLONGDOUBLE);
					break;
				}
				case D_POINTER:
				case D_FUNCPTR:
				{/* src is unsigned integer */
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				default:
					PERROR(pName ":ERROR: Line:%d bad conversion3\n", iv->lastline);
			}
			break;
		}
		case	D_POINTER:
		case	D_FUNCPTR:
		{/* dest is unsigned integer */
			if(dsize == 1)
				obuf[1] = BYTE;
			else if(dsize == 2)
				obuf[1] = SHORT;
			else if(dsize == 4)
				obuf[1] = LONG;
			else if(dsize == 8)
				obuf[1] = CLONGLONG;
			switch(sdtype)
			{
				case D_SIGNED:
				{/* src is signed integer */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(BYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(SHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(LONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CLONGLONG);
					break;
				}
				case D_UNSIGNED:
				case D_SEGMENT:
				{/* src is unsigned integer */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				case D_FLOAT:
				{/* src is floating point */
					if(ssize == 4)
						obuf[1] |= SRC(FLOAT);
					else if(ssize == 8)
						obuf[1] |= SRC(DOUBLE);
					else
						obuf[1] |= SRC(CLONGDOUBLE);
					break;
				}
				case D_POINTER:
				case D_FUNCPTR:
				{/* src is unsigned integer */
					if(dsize <= ssize)
						return;
					if(ssize == 1)
						obuf[1] |= SRC(UBYTE);
					else if(ssize == 2)
						obuf[1] |= SRC(USHORT);
					else if(ssize == 4)
						obuf[1] |= SRC(ULONG);
					else if(ssize == 8)
						obuf[1] |= SRC(CULONGLONG);
					break;
				}
				default:
					PERROR(pName ":ERROR: Line:%d bad conversion4\n", iv->lastline);
			}
			break;
		}
		default:
			PRINTF("Line:%d no conversion\n", iv->lastline);
			return;
	}
	write_obuf(iv, obuf, 2);
#undef SRC
}
static int
check_assignment_conversion(PND dst, PND src)
{
long dsize = dst->size;
long ssize = src->size;
unsigned char ddtype = dst->dtype;
unsigned char sdtype = src->dtype;
int ret = 0;

	if(src->atype & (A_ABSOLUTE|A_POINTER|A_VALUE) == (A_POINTER|A_VALUE))
		return 1;
	switch(ddtype)
	{
		case	D_SIGNED:			/* dest is signed integer */
		{
			switch(sdtype)
			{
				case D_SIGNED:
				case D_UNSIGNED:
				case D_SEGMENT:
				case D_POINTER:
				case D_FUNCPTR:
				{
					if(dsize > ssize)
						ret = 1;
					break;
				}
				case D_FLOAT:
				{
					ret = 1;
					break;
				}
			}
			break;
		}
		case	D_UNSIGNED:	/* dest is unsigned integer */
		case	D_SEGMENT:
		{
			switch(sdtype)
			{
				case D_SIGNED:
				case D_UNSIGNED:
				case D_SEGMENT:
				case D_POINTER:
				case D_FUNCPTR:
				{
					if(dsize > ssize)
						ret = 1;
					break;
				}
				case D_FLOAT:
				{
					ret = 1;
					break;
				}
			}
			break;
		}
		case	D_FLOAT:	/* dest is float, double, long double */
		{
			if(sdtype == D_FLOAT)
			{
				if(dsize != ssize)
					ret = 1;
			}
			else ret = 1;
			break;
		}
		case	D_POINTER:
		case	D_FUNCPTR:		/* dest is pointer */
		{
			switch(sdtype)
			{
				case D_SIGNED:
				case D_UNSIGNED:
				case D_SEGMENT:
				case D_POINTER:
				case D_FUNCPTR:
				{
					if(dsize > ssize)
						ret = 1;
					break;
				}
				case D_FLOAT:
				{
					ret = 1;
					break;
				}
			}
			break;
		}
	}
	return ret;
}
static int
check_binop_conversion(PND dst, PND src)
{
long dsize = dst->size;
long ssize = src->size;
unsigned char ddtype = dst->dtype;
unsigned char sdtype = src->dtype;
int ret = 0;

	switch(ddtype)
	{
		case	D_SIGNED:			/* dest is signed integer */
		{
			switch(sdtype)
			{
				case D_SIGNED:
				case D_UNSIGNED:
				case D_SEGMENT:
				case D_POINTER:
				case D_FUNCPTR:
				{
					if(dsize > ssize)
						ret = 1;
					break;
				}
			}
			break;
		}
		case	D_UNSIGNED:	/* dest is unsigned integer */
		case	D_SEGMENT:
		{
			switch(sdtype)
			{
				case D_SIGNED:
				case D_UNSIGNED:
				case D_SEGMENT:
				case D_POINTER:
				case D_FUNCPTR:
				{
					if(dsize > ssize)
						ret = 1;
					break;
				}
			}
			break;
		}
		case	D_FLOAT:	/* dest is float, double, long double */
		{
			if(sdtype == D_FLOAT)
			{
				if(dsize > ssize)
					ret = 1;
			}
			else ret = 1;
			break;
		}
		case	D_POINTER:
		case	D_FUNCPTR:		/* dest is pointer */
		{
			switch(sdtype)
			{
				case D_SIGNED:
				case D_UNSIGNED:
				case D_SEGMENT:
				case D_POINTER:
				case D_FUNCPTR:
				{
					if(dsize > ssize)
						ret = 1;
					break;
				}
			}
			break;
		}
	}
	return ret;
}
static int
get_size(int type, int size)
{
	switch(type)
	{
		case	D_ARRAY:
		case	D_STRUCT:
		case	D_FUNCTION:
			return B4;

		default:
			switch(size)
			{
				case 1:
					return B1;
				case 2:
					return B2;
				case 4:
					return B4;
				case 8:
					return B8;
				default:
#if SUPPORT_LONG_DOUBLE
					return BX;
#endif
			}
	}
	return 0;
}
static int
load_addr(Piv iv, unsigned char *poc, void *buf, PND pnd, int shft)
{/* Use the shortest possible representation */
long offset = pnd->OFFSET;
long ofs = offset >> 2;

	if(pnd->atype & (A_EXTERN|A_ABSOLUTE))
	{/* 4 bytes for absolute values */
		*poc |= 3<<shft;
		*((long*)buf) = offset;
		iv->extmark += 1;
		iv->markedsym[iv->extmark] = pnd->SYMNUM;
		iv->markedbuf[iv->extmark] = buf;
		return 4;
	}
	if(offset & 0xff000000)
	{/* Relative values are restricted to 28 bits max */
		PERROR(pName ":ERROR: Line:%d offset too large %x\n", iv->lastline, ofs);
		return 0;
	}
	if(offset & 0xffff0000 || offset & 0x00000003)
	{/* 3 bytes for large values and non-aligned values */
		*poc |= 2<<shft;
		*((long*)buf) = offset;
		return 3;
	}
	else if(ofs & 0xffffff00)
	{/* 2 or fewer bytes for small aligned values */
		*poc |= 1<<shft;
		*((short*)buf) = (short)ofs;
		return 2;
	}
	else
	{
		*((char*)buf) = (char)ofs;
		return 1;
	}

}
static void
load_val(Piv iv, unsigned long val)
{
unsigned char obuf[10];

	obuf[0] = LUI;
	++iv->stackdepth;
	if(val & 0xffff0000)
	{
		obuf[0] |= 2;
		*((unsigned long*)&obuf[1]) = val;
		write_obuf(iv, obuf, 5);
	}
	else if(val & 0xffffff00)
	{
		obuf[0] |= 1;
		*((unsigned short*)&obuf[1]) = (unsigned short)val;
		write_obuf(iv, obuf, 3);
	}
	else
	{
		obuf[1] = (unsigned char)val;
		write_obuf(iv, obuf, 2);
	}
}
static int
load_immed(Piv iv, unsigned char *poc, void *obuf, PND pnd, int osize)
{
unsigned short dtype = pnd->dtype;
unsigned char mosize = pnd->opsize;
void *ibuf = pnd->data;

	if(osize >= 0)
	{/* force the immediate value to conform to osize */
		if(osize == mosize)
		{
			switch(osize)
			{
				case	B1:
					*((char*)obuf) = *((char*)ibuf);
					return 1;			
				case	B2:
					*((short*)obuf) = *((short*)ibuf);
					return 2;
				case	B4:
					*((long*)obuf) = *((long*)ibuf);
					return 4;
				case	B8:
					*((double*)obuf) = *((double*)ibuf);
					return 8;
				case	BX:
#if SUPPORT_LONG_DOUBLE
					*((long double*)obuf) = *((long double*)ibuf);
#else
					memcpy(obuf, ibuf, XSZ);
#endif
					return XSZ;
				default:
					PERROR(pName ":ERROR: Line:%d LOAD IMMED SYSERR osize=%d\n", iv->lastline, osize);
			}
		}
		else /* osize != mosize */
		{
			if(dtype == D_FLOAT)
			{
			float f;
			double d;
#if SUPPORT_LONG_DOUBLE
			long double ld;
#endif
				switch(osize)
				{
					case	B4:
						switch(mosize)
						{
							case	B8:
								f = (float)*((double*)ibuf);
								*((float*)obuf) = f;
								return 4;
							case	BX:
#if SUPPORT_LONG_DOUBLE
								f = (float)*((long double*)ibuf);
								*((float*)obuf) = f;
								return 4;
#else
								PERROR(pName ":ERROR: Line:%d long double conversion not supported\n", iv->lastline);
#endif
							default:
								PERROR(pName ":ERROR: Line:%d bad floating immediate input\n",iv->lastline);
						}
					case	B8:
						switch(mosize)
						{
							case	B4:
								d = (double)*((float*)ibuf);
								*((double*)obuf) = d;
								return 8;
							case	BX:
#if SUPPORT_LONG_DOUBLE
								d = (double)*((long double*)ibuf);
								*((double*)obuf) = d;
								return 8;
#else
								PERROR(pName ":ERROR: Line:%d long double conversion not supported\n", iv->lastline);
#endif
							default:
								PERROR(pName ":ERROR: Line:%d bad floating immediate input\n", iv->lastline);
						}
					case	BX:
#if SUPPORT_LONG_DOUBLE
						switch(mosize)
						{
							case	B4:
								ld = (long double)*((float*)ibuf);
								*((long double*)obuf) = ld;
								return XSZ;
							case	B8:
								ld = (long double)*((double*)ibuf);
								*((long double*)obuf) = ld;
								return XSZ;
							default:
								PERROR(pName ":ERROR: Line:%d bad floating immediate input\n", iv->lastline);
						}
						break;
#else
						PERROR(pName ":ERROR: Line:%d long double conversion not supported\n", iv->lastline);
#endif
						break;
					default:
						PERROR(pName ":ERROR: Line:%d bad floating immediate output\n", iv->lastline);
				}
			}/* END: dtype == D_FLOAT */
			else if(dtype == D_UNSIGNED || dtype == D_POINTER)
			{
				switch(osize)
				{
					case B1:
						*((unsigned char*)obuf) = *((unsigned char *)ibuf);
						return 1;
					case B2:
						switch(mosize)
						{
							case B1:
								*((unsigned short*)obuf) = *((unsigned char*)ibuf);
								return 2;
							case B4:
							case B8:
								*((unsigned short*)obuf) = *((unsigned short*)ibuf);
								return 2;
							case BX:
								PERROR(pName ":ERROR: Line:%d invalid integer size\n", iv->lastline);
							default:
								PERROR(pName ":ERROR: Line:%d invalid immediate input\n", iv->lastline);
						}
					case B4:
						switch(mosize)
						{
							case B1:
								*((unsigned long*)obuf) = *((unsigned char*)ibuf);
								return 4;
							case B2:
								*((unsigned long*)obuf) = *((unsigned short*)ibuf);
								return 4;
							case B8:
								*((unsigned long*)obuf) = *((unsigned long*)ibuf);
								return 4;
							case BX:
								PERROR(pName ":ERROR: Line: invalid integer size\n", iv->lastline);
							default:
								PERROR(pName ":ERROR: Line:%d invalid immediate input\n", iv->lastline);
						}
					case B8:
					{
					unsigned long l[2];
						l[0] = 0;
						l[1] = 0;
						if(mosize == B4)
							l[0] = *((unsigned long*)ibuf);
						else if(mosize == B2)
							l[0] = *((unsigned short*)ibuf);
						else if(mosize == B1)
							l[0] = *((unsigned char*)ibuf);
						else
							PERROR(pName ":ERROR: Line:%d invalid integer size\n", iv->lastline);
						memcpy(obuf, l, 8);
						return 8;
					}
					default:
					PERROR(pName ":ERROR: Line:%d invalid integer size=%d\n",
							iv->lastline, osize);
				}
			}
			else /* signed integer */
			{
				switch(osize)
				{
					case B1:
						*((char*)obuf) = *((char *)ibuf);
						return 1;
					case B2:
						switch(mosize)
						{
							case B1:
								*((short*)obuf) = *((char*)ibuf);
								return 2;
							case B4:
							case B8:
								*((short*)obuf) = *((short*)ibuf);
								return 2;
							case BX:
								PERROR(pName ":ERROR: Line:%d invalid integer size\n", iv->lastline);
							default:
								PERROR(pName ":ERROR: Line:%d invalid immediate input\n",iv->lastline);
						}
					case B4:
						switch(mosize)
						{
							case B1:
								*((long*)obuf) = *((char*)ibuf);
								return 4;
							case B2:
								*((long*)obuf) = *((short*)ibuf);
								return 4;
							case B8:
								*((long*)obuf) = *((long*)ibuf);
								return 4;
							case BX:
								PERROR(pName ":ERROR: Line:%d invalid integer size\n", iv->lastline);
							default:
								PERROR(pName ":ERROR: Line:%d invalid immediate input\n", iv->lastline);
						}
					case B8:
					{
					long l[2];
						l[0] = 0;
						l[1] = 0;
						if(mosize == B4)
							l[0] = *((long*)ibuf);
						else if(mosize == B2)
							l[0] = *((short*)ibuf);
						else if(mosize == B1)
							l[0] = *((char*)ibuf);
						else
							PERROR(pName ":ERROR: Line:%d invalid integer size\n", iv->lastline);
						if(l[0] < 0)
							l[1] = -1;
						memcpy(obuf, l, 8);
						return 8;
					}
					default:
					  PERROR(pName ":ERROR: Line:%d invalid integer size=%d\n",
					  	iv->lastline, osize);
				}
			}/* END: signed integer */
		}/* END: osize != mosize */
		/* NOT REACHED */
		PERROR(pName ":ERROR: Line:%d LOAD IMMED SYSERR1 osize=%d mosize=%d\n", iv->lastline, osize, mosize);
		return 0; /* suppress compiler warning */
	}
	else
	{/* generate the shortest possible immediate value */
	int thesize = 0;
		if(dtype == D_FLOAT)
		{
			switch(mosize)
			{
				case	B4:
					*((float*)obuf) = *((float*)ibuf);
					thesize = 4;
					break;
				case	B8:
					*((double*)obuf) = *((double*)ibuf);
					thesize = 8;
					break;
				case	BX:
#if SUPPORT_LONG_DOUBLE
					*((long double*)obuf) = *((long double*)ibuf);
#else
					memcpy(obuf, ibuf, XSZ);
#endif
					thesize = XSZ;
					break;
				default:
					PERROR(pName ":ERROR: Line:%d wrong sized floating immediate\n", iv->lastline);
			}
		}
		else /* dtype not D_FLOAT */
		{
		int negative;
		int sgned;
		unsigned long ul[2];
		long *sl;

			ul[1] = 0;		
			negative = 0;
			sl = ul;
			if(dtype == D_UNSIGNED || dtype == D_POINTER)
			{
				sgned = 0;
				if(mosize == B8)
				{
					ul[0] = *((unsigned long*)ibuf);
					ul[1] = *((unsigned long*)(((char*)ibuf)+4));
				}
				else
				{
					if(mosize == B4)
						ul[0] = *((unsigned long*)ibuf);
					else if(mosize == B2)
						ul[0] = *((unsigned short*)ibuf);
					else if(mosize == B1)
						ul[0] = *((unsigned char*)ibuf);
					else
						PERROR(pName ":ERROR: Line:%d invalid integer immediate\n", iv->lastline);
				}
			}
			else /* sgned */
			{
				sgned = 1;
				if(mosize == B8)
				{
					sl[0] = *((long*)ibuf);
					sl[1] = *((long*)(((char*)ibuf)+4));
					if(sl[1] < 0)
					{
						negative = 1;
						ul[1] = ~ul[1];
						ul[0] = ~ul[0];
						if(ul[0] == 0xffffffff)
						{
							ul[0] = 0;
							ul[1] += 1;
						}
						else ul[0] += 1;
					}
				}
				else
				{
					if(mosize == B4)
						sl[0] = *((long*)ibuf);
					else if(mosize == B2)
						sl[0] = *((short*)ibuf);
					else if(mosize == B1)
						sl[0] = *((char*)ibuf);
					else
						PERROR(pName ":ERROR: Line:%d invalid integer immediate\n",iv->lastline);
					if(sl[0] < 0)
					{
						negative = 1;
						sl[0] = -sl[0];
					}
				}
			} /* END: sgned */
			if(negative)
			{
				if(ul[1])
					mosize = B8;
				else if(ul[0] & 0xffff8000)
					mosize = B4;
				else if(ul[0] & 0xffffff80)
					mosize = B2;
				else
					mosize = B1;
			}
			else
			{
				if(sgned)
				{
					if(ul[1])
						mosize = B8;
					else if(ul[0] & 0xffff8000)
					{
						if(!(ul[0] & 0xffff0000))
						{
							*poc = LUI;
							mosize = B2;
						}
						else
							mosize = B4;
					}
					else if(ul[0] & 0xffffff80)
					{
						if(!(ul[0] & 0xffffff00))
						{
							*poc = LUI;
							mosize = B1;
						}
						else
							mosize = B2;
					}
					else
						mosize = B1;
				}
				else /* unsigned */
				{
					if(ul[1])
						mosize = B8;
					else if(ul[0] & 0xffff0000)
						mosize = B4;
					else if(ul[0] & 0xffffff00)
						mosize = B2;
					else
						mosize = B1;
				}
			}
			if(negative)
			{
				ul[1] = ~ul[1];
				ul[0] = ~ul[0];
				if(ul[0] == 0xffffffff)
				{
					ul[0] = 0;
					ul[1] += 1;
				}
				else ul[0] += 1;
			}
			switch(mosize)
			{
				case B1:
					*((char*)obuf) = *((char*)sl);
					thesize = 1;
					break;
				case B2:
					*((short*)obuf) = *((short*)sl);
					thesize = 2;
					break;
				case B4:
					*((long*)obuf) = *((long*)sl);
					thesize = 4;
					break;
				case B8:
					*((double*)obuf) = *((double*)sl);
					thesize = 8;
					break;
			}
		}/* END: not D_FLOAT */
		*poc |= mosize;
		return thesize;
	}
}
static void
addr_toevalstack(Piv iv, PND pnd)
{
unsigned char obuf[20];
int atype = pnd->atype;

	if(atype & (A_AUTO|A_PARAM|A_DATA))
	{
	int l;
		obuf[0] = LAI;
		l = 1;
		l += load_addr(iv, &obuf[0], &obuf[1], pnd, 0);

		write_obuf(iv, obuf, l);
		if(atype & (A_AUTO|A_PARAM))
		{
			obuf[0] = ABSSTK;
			write_obuf(iv, obuf, 1);
		}
		else if(l < 5)
		{
			obuf[0] = ABSMEM;
			write_obuf(iv, obuf, 1);
		}
		++iv->stackdepth;
	}
}
static void
data_toevalstack(Piv iv, PND pnd, PND dst)
{
unsigned char obuf[20];
int xtra, oc;
unsigned short dtype, atype;
unsigned char osize;

		atype = pnd->atype;
		dtype = pnd->dtype;
		if(atype & A_IMMED)
		{
			if(dtype == D_UNSIGNED)
				obuf[0] = LUI;
			else
				obuf[0] = LI;
			xtra = load_immed(iv, &obuf[0], &obuf[1], pnd, -1);
			write_obuf(iv, obuf, 1+xtra);
			++iv->stackdepth;
			return;
		}
		xtra = oc = 0;
		osize = pnd->opsize;
		if(osize == BX)
		{
			obuf[0] = XTD;
			osize = 0;
			oc = 1;
			xtra = 1;
		}
		if(atype & (A_TEMP|A_RET))
		{
			if(atype & A_MEMADDR)
			{
			unsigned char xsize = get_datasize(0, pnd);
				if(osize == B8 && dtype < D_FLOAT)
					obuf[xtra++] = XTD;

				if(		dst	
					&&	(pnd->TMPNUM == dst->TMPNUM)
					&&	((dst->atype & (A_ABSOLUTE|A_MEMADDR))
							== (A_ABSOLUTE|A_MEMADDR))
				  )
				{/* generally used for a += b etc. */
					obuf[xtra++] = IMMED;
					obuf[xtra] = DEREF1|xsize;
					write_obuf(iv, obuf, 1+xtra);
					++iv->stackdepth;
				}
				else
				{
					obuf[xtra++] = IMMED;
					obuf[xtra] = DEREF|get_datasize(0, pnd);
					write_obuf(iv, obuf, 1+xtra);
				}
				pnd->atype &= ~A_MEMADDR;
				pnd->atype |= A_VALUE;
			}
			return;
		}

		if(atype & (A_AUTO|A_PARAM))
		{
			if(atype & (A_POINTER|A_VALUE) == (A_POINTER|A_VALUE))
			{/* Put address on eval stack */
				addr_toevalstack(iv, pnd);
				pnd->atype |= A_ABSOLUTE;
				return;
			}
			else
			{
				obuf[oc] = LS|osize;
				xtra += load_addr(iv, &obuf[oc], &obuf[oc+1], pnd, 2);
				++iv->stackdepth;
			}
		}
		else if(atype & A_DATA)
		{
			if(atype & (A_POINTER|A_VALUE) == (A_POINTER|A_VALUE))
			{/* Put address on eval stack */
				addr_toevalstack(iv, pnd);
				pnd->atype |= A_ABSOLUTE;
				return;
			}
			else
			{
				obuf[oc] = LM|osize;
				xtra += load_addr(iv,&obuf[oc], &obuf[oc+1], pnd, 2);
				++iv->stackdepth;
			}
		}
		write_obuf(iv, obuf, 1+xtra);
}
static void
promote_arg(Piv iv, PND pnd, long argsize)
{
long dsize = pnd->size;
struct _nd dst;

	if(dsize != argsize)
	{
		dst.size = argsize;
		dst.dtype = pnd->dtype;
		dst.atype = 0;
		do_conversion(iv, &dst, pnd);
	}
}
static unsigned char
arg_toevalstack(Piv iv, PND pnd, long argsize)
{
unsigned char dtype;

	if(pnd->atype & A_IMMED)
	{
		data_toevalstack(iv, pnd, 0);
		return ARG;
	}
	dtype = pnd->dtype;
	if(dtype == D_STRUCT)
	{
		addr_toevalstack(iv, pnd);
		return ARGA;
	}
	else if(dtype == D_FUNCTION)
	{
		addr_toevalstack(iv, pnd);
		return ARGF;
	}
	else if(dtype == D_FUNCPTR)
	{
		data_toevalstack(iv, pnd, 0);
		return ARGF;
	}
	else if(dtype == D_ARRAY)
	{
		addr_toevalstack(iv, pnd);
	}
	else
	{
		data_toevalstack(iv, pnd, 0);
		promote_arg(iv, pnd, argsize);
	}
	return ARG;
}
static void
mov_esdata(Piv iv, unsigned short atype, int size)
{
unsigned char obuf[2];

	if(atype & A_MEMADDR)
	{
		if(size == B1)
			obuf[0] = MOVAA1;
		else if(size == B2)
			obuf[0] = MOVAA2;
		else if(size == B4)
			obuf[0] = MOVAA4;
		else if(size == B8)
			obuf[0] = MOVAA8;
#if SUPPORT_LONG_DOUBLE
		else if(size == BX)
			obuf[0] = MOVAAX;
#endif
		else {obuf[0] = MOVAAC; size = XSZ;}
	}
	else
	{
		if(size == B1)
			obuf[0] = MOVDA1;
		else if(size == B2)
			obuf[0] = MOVDA2;
		else if(size == B4)
			obuf[0] = MOVDA4;
		else if(size == B8)
			obuf[0] = MOVDA8;
#if SUPPORT_LONG_DOUBLE
		else if(size == BX)
			obuf[0] = MOVDAX;
#endif
		else
			PERROR(pName ":ERROR: Line:%d illegal mov data size=%d\n", iv->lastline, size);
	}
	if(obuf[0] == MOVAAC)
	{
		load_val(iv, size);
		--iv->stackdepth;
	}
	write_obuf(iv, obuf, 1);	
	iv->stackdepth -= 2;
}
static void
from_evalstack(Piv iv, PND d, PND s)
{
unsigned char obuf[20];
int xtra, oc;
unsigned short datype = d->atype;
unsigned char osize = d->opsize;

	xtra = oc = 0;

	if(datype & (A_TEMP|A_RET))
	{
		if(datype & A_MEMADDR)
		{
			mov_esdata(iv, s->atype, osize);
		}
		return;
	}

	if(osize == BX)
	{
		obuf[0] = XTD;
		oc = 1;
		osize = 0;
		xtra = 1;
	}
	if(datype & (A_AUTO|A_PARAM))
	{
		obuf[oc] = SS|osize;
		xtra += load_addr(iv, &obuf[oc], &obuf[oc+1], d, 2);
		--iv->stackdepth;
	}
	else if(datype & OPDATA)
	{
		obuf[oc] = SM|osize;
		xtra += load_addr(iv, &obuf[oc], &obuf[oc+1], d, 2);
		--iv->stackdepth;
	}
	write_obuf(iv, obuf, 1+xtra);
}
static unsigned char
get_datasize(unsigned char opcode, PND pnd)
{
unsigned char dtype = pnd->dtype;
long dsize = pnd->size;

	switch(dtype)
	{
		case	D_ARRAY:
		case	D_FUNCTION:
		case	D_STRUCT:
			return LONG;
	}

	if(		opcode == orop
		||	opcode == andop
		||	opcode == notop
		||	opcode == lshop
		||	opcode == complop
		||	opcode == xorop
		)
	{
		if(dsize == 1)
			return B1;
		else if(dsize == 2)
			return B2;
		else if(dsize == 4)
			return B4;
		else if(dsize == 8)
			return B8;		
		else
			return BX;
	}
	else
	{
		if(dtype == D_UNSIGNED)
		{
			if(dsize == 1)
				return UBYTE;
			else if(dsize == 2)
				return USHORT;
			else if(dsize == 4)
				return ULONG;
			else
			{
				if(opcode == rshop)
					return SULONGLONG;
				return ULONGLONG;
			}
		}
		else if(dtype == D_FLOAT)
		{
			if(dsize == 4)
				return FLOAT;
			else if(dsize == 8)
				return DOUBLE;
			else
				return LONGDOUBLE;
		}
		else
		{
			if(dsize == 1)
				return BYTE;
			else if(dsize == 2)
				return SHORT;
			else if(dsize == 4)
				return LONG;
			else
			{
				if(opcode == rshop)
					return SLONGLONG;
				return LONGLONG;
			}
		}
	}
	return 0;
}
static void *
new_nodeO(Piv iv)
{
PNODEO p;

	if(iv->ob_bufcnt < sizeof(NODEO))
	{/* Allocate a new chunk of linked list space */
	  iv->ob_bufcnt = 4080;
	  iv->ob_buf = Ccalloc(FUNCDATA, 1, iv->ob_bufcnt);
	}
	p = (PNODEO)iv->ob_buf;
	iv->ob_buf += sizeof(NODEO);
	iv->ob_bufcnt -= sizeof(NODEO);
	return p;	
}
static void
link_ob(Piv iv)
{/* Attach to the used list */

	if(!iv->ob_usedhead)
	{
		iv->ob_usedhead = iv->ob;
		iv->ob_usedtail = iv->ob;
	}
	else
	{
		iv->ob_usedtail->next = iv->ob;
		iv->ob_usedtail = iv->ob;
	}
	iv->ob = new_nodeO(iv);
}
static void *
new_nodeC(Piv iv)
{
PNODEC p;

	if(iv->cod_bufcnt < sizeof(NODEC))
	{/* Allocate a new chunk of linked list space */
	  iv->cod_bufcnt = 4080;
	  iv->cod_buf = Ccalloc(FUNCDATA, 1, iv->cod_bufcnt);
	}
	p = (PNODEC)iv->cod_buf;
	iv->cod_buf += sizeof(NODEC);
	iv->cod_bufcnt -= sizeof(NODEC);
	return p;	
}
static void
link_cod(Piv iv)
{/* Attach to the used list */

	if(!iv->cod_usedhead)
	{
		iv->cod_usedhead = iv->cod;
		iv->cod_usedtail = iv->cod;
	}
	else
	{
		iv->cod_usedtail->next = iv->cod;
		iv->cod_usedtail = iv->cod;
	}
	iv->cod = new_nodeC(iv);
}
static PNODEC
gen_inst(Piv iv, PNODEO pnode)
{
unsigned char *p;
unsigned char obuf[40];
int xtra, oc;
unsigned char opcode;
PND d,l,r;

	d = &pnode->d;
	l = &pnode->l;
	r = &pnode->r;
	p = pnode->p;
	opcode = *p;

	xtra = oc = 0;
	
	switch(opcode)
	{
		case regainop:
			obuf[0] = REGAIN;
			write_obuf(iv, obuf, 1);
			++iv->stackdepth;
			break;
		case grabop:
			obuf[0] = SWAP4DEEP;
			write_obuf(iv, obuf, 1);
			break;
		case getvalop:
		case derefop:
		case assignop:
		case duptmpop:
		{
			if(		opcode == getvalop 
				&&	!(l->atype & A_IMMED)
				&&  l->dtype == D_FUNCTION)
			{
				addr_toevalstack(iv, l);
			}
			else if(	opcode != derefop 
					&&	!(l->atype & A_IMMED)
					&& check_assignment_conversion(d, l))
			{/* Must move data through evaluation stack */
				data_toevalstack(iv, l, d);
				do_conversion(iv, d, l);
				l->atype &= ~A_MEMADDR;
				l->atype |= A_VALUE;
				if(opcode == assignop)
				  from_evalstack(iv, d, l);
			}
			else
			{/* Can move data directly from memory to memory */
			unsigned char ddtype = d->dtype;
			unsigned char osize = d->opsize;
			unsigned short latype = l->atype;
			unsigned short datype = d->atype;
				if(osize == BX)
				{
					obuf[0] = XTD;
					oc = 1;
					osize = 0;
					xtra = 1;
				}
				if(latype & A_IMMED)
				{
					if(datype & (A_TEMP|A_RET))
					{
						if(l->dtype == D_UNSIGNED)
							obuf[oc] = LUI;
						else
							obuf[oc] = LI;
						xtra += load_immed(iv, &obuf[oc], &obuf[oc+1], l, -1);
						++iv->stackdepth;
						if(datype & A_MEMADDR)
						{
							write_obuf(iv, obuf, 1+xtra);
							mov_esdata(iv, 0, osize);
							break;
						}
					}
					else if(datype & (A_AUTO|A_PARAM))
					{
					int lx;
						obuf[0] = IMMED;
						oc = 1;
						xtra = 1;
						obuf[oc] = SSI|osize;
						lx = load_addr(iv, &obuf[oc], &obuf[oc+1], d, 2);
						lx += load_immed(iv, &obuf[oc], &obuf[oc+1+lx], l, osize);
						xtra += lx;
					}
					else if(datype & A_DATA)
					{
					int lx;
						obuf[0] = IMMED;
						oc = 1;
						xtra = 1;
						obuf[oc] = SMI|osize;
						lx = load_addr(iv, &obuf[oc], &obuf[oc+1], d, 2);
						lx += load_immed(iv, &obuf[oc], &obuf[oc+1+lx], l, osize);
						xtra += lx;
					}
					else
					{
						return iv->cod_usedtail;
					}
				}
				else if(latype & (A_TEMP|A_RET))
				{
					if(datype & (A_TEMP|A_RET))
					{
						if(opcode == duptmpop)
						{
							if(osize <= B4)
								obuf[0] = DUP4;
							else
								obuf[0] = DUP;
							xtra = 0;
							++iv->stackdepth;
						}
						else if(opcode == getvalop || opcode == derefop)
						{
							if(latype & A_MEMADDR)
							{
							unsigned char xsize = get_datasize(0, l);
								if(osize == B8 && ddtype < D_FLOAT)
									obuf[xtra++] = XTD;
								if(datype & A_MEMADDR)
									xsize = ULONG;
								obuf[xtra++] = IMMED;
								obuf[xtra] = DEREF|xsize;
							}
							else return iv->cod_usedtail;	/* nop */
						}
						else if(datype & A_MEMADDR)
						{
							mov_esdata(iv, latype, osize);
							break;
						}
						else
						{/* move a value temp to a value temp, effective nop */
							return iv->cod_usedtail;
						}
					}
					else if(datype & (A_AUTO|A_PARAM))
					{
						if(latype & A_MEMADDR)
						{
						unsigned char xsize = get_datasize(0, l);
						int qc = 1;
							if(		osize == BX
								||	(osize == B8 && ddtype < D_FLOAT))
								obuf[qc++] = XTD;
							obuf[qc++] = IMMED;
							obuf[qc] = DEREF|xsize;
							write_obuf(iv, obuf+1, qc);
						}
						obuf[oc] = SS|osize;
						xtra += load_addr(iv, &obuf[oc], &obuf[oc+1], d, 2);
						--iv->stackdepth;
					}
					else if(datype & A_DATA)
					{
						if(latype & A_MEMADDR)
						{
						unsigned char xsize = get_datasize(0, l);
						int qc = 1;
							if(		osize == BX
								||	(osize == B8 && ddtype < D_FLOAT))
								obuf[qc++] = XTD;
							obuf[qc++] = IMMED;
							obuf[qc] = DEREF|xsize;
							write_obuf(iv, obuf+1, qc);
						}
						obuf[oc] = SM|osize;
						xtra += load_addr(iv, &obuf[oc], &obuf[oc+1], d, 2);
						--iv->stackdepth;
					}
					else
					{
						return iv->cod_usedtail;
					}
				}
				else if(latype & (A_AUTO|A_PARAM))
				{
				int lx;
					if((latype & (A_POINTER|A_VALUE)) == (A_POINTER|A_VALUE))
					{/* Put address on eval stack */
						addr_toevalstack(iv, l);
						if(opcode == assignop)
						  from_evalstack(iv, d, l);
						break;
					}
					if(datype & (A_TEMP|A_RET))
					{
						if(opcode == derefop)
							osize = B4;
						obuf[oc] = LS|osize;
						xtra += load_addr(iv, &obuf[oc], &obuf[oc+1], l, 2);
						++iv->stackdepth;
						if(opcode == assignop)
						{
							/* Here we need a 'store from stack addr' inst */
							if(datype & A_MEMADDR)
							{/* Address was on the eval stack */
								write_obuf(iv, obuf, 1+xtra);
								mov_esdata(iv, 0, osize);
								break;
							}
						}
					}
					else if(datype & (A_AUTO|A_PARAM))
					{
						obuf[oc] = MOVSS|osize;
						obuf[++oc] = 0;
						lx = load_addr(iv, &obuf[oc], &obuf[oc+1], l, 2);
						lx += load_addr(iv, &obuf[oc], &obuf[oc+1+lx], d, 0);
						xtra += lx+1;
					}
					else if(datype & A_DATA)
					{
						obuf[oc] = MOVSM|osize;
						obuf[++oc] = 0;
						lx = load_addr(iv, &obuf[oc], &obuf[oc+1], l, 2);
						lx += load_addr(iv, &obuf[oc], &obuf[oc+1+lx], d, 0);
						xtra += lx+1;
					}
					else
					{
						return iv->cod_usedtail;
					}
				}
				else if(latype & A_DATA)
				{
				int lx;
					if((latype & (A_POINTER|A_VALUE)) == (A_POINTER|A_VALUE))
					{/* Put address on eval stack */
						addr_toevalstack(iv, l);
						if(opcode == assignop)
						  from_evalstack(iv, d, l);
						break;
					}
					if(datype & (A_TEMP|A_RET))
					{
						if(opcode == derefop)
							osize = B4;
						obuf[oc] = LM|osize;
						xtra += load_addr(iv, &obuf[oc], &obuf[oc+1], l, 2);
						++iv->stackdepth;
						if(opcode == assignop)
						{
							/* Here we need a 'store from stack addr' inst */
 							if(datype & A_MEMADDR)
							{/* Address was on the eval stack */
								write_obuf(iv, obuf, 1+xtra);
								mov_esdata(iv, 0, osize);
								break;
							}
						}
					}
					else if(datype & (A_AUTO|A_PARAM))
					{
						obuf[oc] = MOVMS|osize;
						obuf[++oc] = 0;
						lx = load_addr(iv, &obuf[oc], &obuf[oc+1], l, 2);
						lx += load_addr(iv, &obuf[oc], &obuf[oc+1+lx], d, 0);
						xtra += lx+1;
					}
					else if(datype & A_DATA)
					{
						obuf[oc] = MOVMM|osize;
						obuf[++oc] = 0;
						lx = load_addr(iv, &obuf[oc], &obuf[oc+1], l, 2);
						lx += load_addr(iv, &obuf[oc], &obuf[oc+1+lx], d, 0);
						xtra += lx+1;
					}
					else
					{
						return iv->cod_usedtail;
					}
				}
				else
				{
					return iv->cod_usedtail;
				}
				write_obuf(iv, obuf, 1+xtra);
			}/* END: move data directly */
			if(opcode == assignop && iv->has_structret)
			{
				obuf[0] = XTD;
				obuf[1] = PRUNESTRUCT;
				write_obuf(iv, obuf, 2);
				iv->has_structret = 0;
			}
			break;
		}
		case plusop:
		case minusop:
		case mulop:
		case divop:
		case orop:
		case xorop:
		case andop:
		case eqop:
		case neqop:
		case ltop:
		case gtop:
		case leop:
		case geop:
		{/* BINOPS */
		unsigned char ddtype;
		unsigned char osize;
		unsigned short latype = l->atype;
		unsigned short ratype = r->atype;
		PND bestptr = 0;

			if(latype & A_IMMED || ratype & A_IMMED)
			{
			  if(ratype & A_IMMED)
			  {/* IMMEDIATE IS ON THE RIGHT */
			  bestptr = l;

				data_toevalstack(iv, l, d);
				if(check_binop_conversion(r, l))
					do_conversion(iv, r, l);

				data_toevalstack(iv, r, d);

			    ddtype = bestptr->dtype;
			    osize = l->opsize;

				if(osize == BX || (osize == B8 && ddtype != D_FLOAT))
					obuf[oc++] = XTD;
				obuf[oc++] = binops[opcode]|get_datasize(opcode, l);
				--iv->stackdepth;
				write_obuf(iv, obuf, oc);
			  }
			  else /* IMMEDIATE IS ON THE LEFT */
			  {
				bestptr = r;
				if(ratype & (A_TEMP|A_RET))
				{
					data_toevalstack(iv, r, d);
					if(check_binop_conversion(l, r))
						do_conversion(iv, l, r);
					data_toevalstack(iv, l, d);

				    ddtype = bestptr->dtype;
				    osize = r->opsize;

					if(		opcode != plusop
						&&	opcode != eqop
						&&	opcode != neqop
						&&	opcode != andop
						&&	opcode != orop
						&&	opcode != mulop
						&&	opcode != xorop
						)
					{/* stack order relevant */
						if(osize <= B4)
							obuf[0] = SWAP4;
						else
							obuf[0] = SWAP;
						write_obuf(iv, obuf, 1);
					}
				}
				else
				{
					data_toevalstack(iv, l, d);
					data_toevalstack(iv, r, d);
					if(check_binop_conversion(l, r))
						do_conversion(iv, l, r);

				    ddtype = r->dtype;
					osize = r->opsize;
				}

				if(osize == BX || (osize == B8 && ddtype != D_FLOAT))
					obuf[oc++] = XTD;
				obuf[oc++] = binops[opcode]|get_datasize(opcode, l);
				write_obuf(iv, obuf, oc);
				--iv->stackdepth;
			  }/* END: IMMEDIATE IS ON LEFT */
			}/* IMMEDIATES */
			else
			{/* NOT IMMEDIATES */

				if(ratype & (A_TEMP|A_RET))
				{/* RIGHT HAND SIDE IS ALREADY ON STACK */

					data_toevalstack(iv, r, d);

					if(latype & (A_TEMP|A_RET))
					{/* LEFT HAND SIDE WAS ALSO ON STACK */

						/* Is it the same data as right hand side ?? */
						if(l->TMPNUM == r->TMPNUM) 
						{
							if(r->opsize <= B4)
								obuf[0] = DUP4;
							else
								obuf[0] = DUP;
							write_obuf(iv, obuf, 1);
							++iv->stackdepth;
							l->atype = r->atype;
						}
						else
						{
						unsigned char lltype = l->dtype;
						unsigned lsize = l->opsize;
							if(latype & A_MEMADDR)
							{
							unsigned char xsize = get_datasize(0, l);
							int qc = 0;

								if(		(l->TMPNUM == d->TMPNUM)
									&&	((d->atype & (A_ABSOLUTE|A_MEMADDR))
											== (A_ABSOLUTE|A_MEMADDR))
				  				  )
								{/* it must expand with a DEREF1 */
									if(lsize <= B4)
										obuf[0] = DUP4;
									else
										obuf[0] = DUP;
									obuf[1] = DUMP;
									obuf[2] = DUMP;
									write_obuf(iv, obuf, 3);

									if(		lsize == BX
										||	(lsize == B8 && lltype < D_FLOAT))
										obuf[qc++] = XTD;
									obuf[qc++] = IMMED;
									obuf[qc++] = DEREF1|xsize;
									write_obuf(iv, obuf, qc);
									++iv->stackdepth;
								}
								else
								{
									obuf[0] = DUMP;
									write_obuf(iv, obuf, 1);

									if(		lsize == BX
										||	(lsize == B8 && lltype < D_FLOAT))
										obuf[qc++] = XTD;
									obuf[qc++] = IMMED;
									obuf[qc++] = DEREF|xsize;
									write_obuf(iv, obuf, qc);
								}
								l->atype &= ~A_MEMADDR;
								l->atype |= A_VALUE;

								if(check_binop_conversion(r, l))
								{
									bestptr = r;
									do_conversion(iv, r, l);
								}
								obuf[0] = REGAIN;
								write_obuf(iv, obuf, 1);

								if(!bestptr)
								 if(check_binop_conversion(l, r))
								 {
									bestptr = l;
									do_conversion(iv, l, r);
								 }
							}
						}
					}
					else
					{/* LEFT HAND SIDE IS NOT ON STACK */

						if(check_binop_conversion(l, r))
						{
							bestptr = l;
							do_conversion(iv, l, r);
						}
						data_toevalstack(iv, l, d);
						if(!bestptr)
						 if(check_binop_conversion(r, l))
						 {
							bestptr = r;
							do_conversion(iv, r, l);
						 }
						if(		opcode != plusop
							&&	opcode != eqop
							&&	opcode != neqop
							&&	opcode != andop
							&&	opcode != orop
							&&  opcode != mulop
							&&	opcode != xorop
							)
						{/* stack order relevant */
							if(r->opsize <= B4)
								obuf[0] = SWAP4;
							else
								obuf[0] = SWAP;
							write_obuf(iv, obuf, 1);
						}
					}
				}
				else /* ONLY LEFT HAND SIDE CAN BE ON STACK */
				{
					data_toevalstack(iv, l, d);
					if(check_binop_conversion(r, l))
					{
						bestptr = r;
						do_conversion(iv, r, l);
					}

					data_toevalstack(iv, r, d);
					if(!bestptr)
					 if(check_binop_conversion(l, r))
					 {
						bestptr = l;
						do_conversion(iv, l, r);
					}
				}
				if(!bestptr)
					bestptr = l;

				ddtype = bestptr->dtype;
				osize = bestptr->opsize;

				if(osize == BX || (osize==B8 && ddtype != D_FLOAT))
					obuf[oc++] = XTD;
				obuf[oc++] = binops[opcode]|get_datasize(opcode, bestptr);
				write_obuf(iv, obuf, oc);
				--iv->stackdepth;
			}/* END: NOT IMMEDIATES */

			if(opcode >= eqop)
			{
				bestptr = &longtype;
			}

			if(check_assignment_conversion(d, bestptr))
				do_conversion(iv, d, bestptr);
			bestptr->atype &= ~A_MEMADDR;
			bestptr->atype |= A_VALUE;
			from_evalstack(iv, d, bestptr);

			break;
		}/* END: BINOPS */

		case lshop:
		case rshop:
		{/* SHIFTS */
		unsigned short latype = l->atype;
		unsigned short ratype = r->atype;
		PND bestptr;


			if(latype & A_IMMED || ratype & A_IMMED)
			{
			  if(ratype & A_IMMED)
			  {/* IMMEDIATE IS ON THE RIGHT */
				data_toevalstack(iv, l, d);

				obuf[0] = XTD;
				if(opcode == lshop)
				{
					obuf[1] = LSHI|get_datasize(opcode, l);
					obuf[2] = r->data->Uuchar;
				}
				else if(opcode == rshop)
				{
					obuf[1] = RSHI|get_datasize(opcode, l);
					obuf[2] = r->data->Uuchar;
				}
				write_obuf(iv, obuf, 3);
			  }
			  else /* IMMEDIATE IS ON THE LEFT */
			  {
				if(ratype & (A_TEMP|A_RET))
				{
					data_toevalstack(iv, r, d);
					data_toevalstack(iv, l, d);
					if(r->opsize <= B4)
						obuf[0] = SWAP4;
					else
						obuf[0] = SWAP;
					write_obuf(iv, obuf, 1);
				}
				else
				{
					data_toevalstack(iv, l, d);
					data_toevalstack(iv, r, d);
				}

				obuf[0] = XTD;
				obuf[1] = binops[opcode]|get_datasize(opcode, l);
				write_obuf(iv, obuf, 2);
				--iv->stackdepth;
			  }
			}
			else
			{/* NOT IMMEDIATES */

				if(ratype & (A_TEMP|A_RET))
				{/* RIGHT HAND SIDE IS ALREADY ON STACK */

					data_toevalstack(iv, r, d);

					if(latype & (A_TEMP|A_RET))
					{/* LEFT HAND SIDE WAS ALSO ON STACK */

						/* Is it the same data as right hand side ?? */
						if(l->TMPNUM == r->TMPNUM)
						{
							if(l->opsize <= B4 && r->opsize <= B4)
								obuf[0] = DUP4;
							else
								obuf[0] = DUP;
							write_obuf(iv, obuf, 1);
							++iv->stackdepth;
							l->atype = r->atype;
						}
						else
						{/* reversed stack and not same data */

							if(latype & A_MEMADDR)
							{
							unsigned char xsize = l->opsize;
							int qc = 0;

								if(		(l->TMPNUM == d->TMPNUM)
									&&	((d->atype & (A_ABSOLUTE|A_MEMADDR))
											== (A_ABSOLUTE|A_MEMADDR))
				  				  )
								{/* it must expand with a DEREF1 */
									if(l->opsize <= B4)
										obuf[0] = DUP4;
									else
										obuf[0] = DUP;
									obuf[1] = DUMP;
									obuf[2] = DUMP;
									write_obuf(iv, obuf, 3);

									if(l->opsize == B8)
										obuf[qc++] = XTD;
									obuf[qc++] = IMMED;
									obuf[qc++] = DEREF1|xsize;
									write_obuf(iv, obuf, qc);
									++iv->stackdepth;
								}
								else
								{
									obuf[0] = DUMP;
									write_obuf(iv, obuf, 1);

									if(l->opsize == B8)
										obuf[qc++] = XTD;
									obuf[qc++] = IMMED;
									obuf[qc++] = DEREF|xsize;
									write_obuf(iv, obuf, qc);
								}
								l->atype &= ~A_MEMADDR;
								l->atype |= A_VALUE;

								obuf[0] = REGAIN;
								write_obuf(iv, obuf, 1);
							}
						}
					}
					else
					{/* LEFT HAND SIDE IS NOT ON STACK */

						data_toevalstack(iv, l, d);
						if(r->opsize <= B4 && l->opsize <= B4)
							obuf[0] = SWAP4;
						else
							obuf[0] = SWAP;
						write_obuf(iv, obuf, 1);
					}
				}
				else /* ONLY LEFT HAND SIDE CAN BE ON STACK */
				{
					data_toevalstack(iv, l, d);
					data_toevalstack(iv, r, d);
				}

				/* ENSURE THAT THE RIGHT SIDE IS A LONG OR A LONG LONG */
				bestptr = (l->size > 4) ? &longlongtype : &longtype;
				if(check_assignment_conversion(bestptr, r))
					do_conversion(iv, bestptr, r);

				obuf[0] = XTD;
				obuf[1] = binops[opcode]|get_datasize(opcode, l);
				write_obuf(iv, obuf, 2);
				--iv->stackdepth;
			}/* END: NOT IMMEDIATES */

			if(check_assignment_conversion(d, l))
				do_conversion(iv, d, l);
			l->atype &= ~A_MEMADDR;
			l->atype |= A_VALUE;
			from_evalstack(iv, d, l);

			break;
		}/* END: SHIFTS */
		case modop:
		{/* MODULUS */
		unsigned short latype = l->atype;
		unsigned short ratype = r->atype;
		PND bestptr;

			if(latype & A_IMMED || ratype & A_IMMED)
			{
			  if(ratype & A_IMMED)
			  {/* IMMEDIATE IS ON THE RIGHT */
				data_toevalstack(iv, l, d);

				if(l->opsize == B8)
				{
					obuf[oc++] = XTD;
				}
				if(d->size == 2)
				{
					obuf[oc++] = IMMED;
					obuf[oc++] = MODI|get_datasize(opcode, l);
					*((short*)&obuf[oc]) = r->data->Uushort;
					xtra = 2;
				}
				else
				{/* no fast instructions available */
					data_toevalstack(iv, r, d);
					obuf[oc++] = binops[opcode]|get_datasize(opcode, l);
					--iv->stackdepth;
				}
				write_obuf(iv, obuf, oc+xtra);
			  }
			  else /* IMMEDIATE IS ON THE LEFT */
			  {

				if(ratype & (A_TEMP|A_RET))
				{
					data_toevalstack(iv, r, d);
					data_toevalstack(iv, l, d);
					if(r->opsize <= B4)
						obuf[0] = SWAP4;
					else
						obuf[0] = SWAP;
					write_obuf(iv, obuf, 1);
				}
				else
				{/* load eval stack without regard for conversion */
					data_toevalstack(iv, l, d);
					data_toevalstack(iv, r, d);
				}

				if(r->opsize == B8)
				{
					obuf[oc++] = XTD;
				}
				obuf[oc++] = binops[opcode]|get_datasize(opcode, l);
				write_obuf(iv, obuf, oc);
				--iv->stackdepth;
			  }
			}
			else
			{/* NOT IMMEDIATES */
				if(ratype & (A_TEMP|A_RET))
				{/* RIGHT HAND SIDE IS ALREADY ON STACK */

					data_toevalstack(iv, r, d);

					if(latype & (A_TEMP|A_RET))
					{/* LEFT HAND SIDE WAS ALSO ON STACK */

						/* Is it the same data as right hand side ?? */
						if(l->TMPNUM == r->TMPNUM)
						{
							if(l->opsize <= B4 && r->opsize <= B4)
								obuf[0] = DUP4;
							else
								obuf[0] = DUP;
							write_obuf(iv, obuf, 1);
							++iv->stackdepth;
							l->atype = r->atype;
						}
						else
						{/* reversed stack and not same data */

							if(latype & A_MEMADDR)
							{
							unsigned char xsize = get_datasize(0, l);
							int qc = 0;

								if(		(l->TMPNUM == d->TMPNUM)	
									&&	((d->atype & (A_ABSOLUTE|A_MEMADDR))
											== (A_ABSOLUTE|A_MEMADDR))
				  				  )
								{/* it must expand with a DEREF1 */
									if(r->opsize <= B4)
										obuf[0] = DUP4;
									else
										obuf[0] = DUP;
									obuf[1] = DUMP;
									obuf[2] = DUMP;
									write_obuf(iv, obuf, 3);

									if(r->opsize == B8)
										obuf[qc++] = XTD;
									obuf[qc++] = IMMED;
									obuf[qc++] = DEREF1|xsize;
									write_obuf(iv, obuf, qc);
									++iv->stackdepth;
								}
								else
								{
									obuf[0] = DUMP;
									write_obuf(iv, obuf, 1);

									if(r->opsize == B8)
										obuf[qc++] = XTD;
									obuf[qc++] = IMMED;
									obuf[qc++] = DEREF|xsize;
									write_obuf(iv, obuf, qc);
								}
								l->atype &= ~A_MEMADDR;
								l->atype |= A_VALUE;

								obuf[0] = REGAIN;
								write_obuf(iv, obuf, 1);
							}
						}
					}
					else
					{/* LEFT HAND SIDE IS NOT ON STACK */

						data_toevalstack(iv, l, d);
						if(r->opsize <= B4 && l->opsize <= B4)
							obuf[0] = SWAP4;
						else
							obuf[0] = SWAP;
						write_obuf(iv, obuf, 1);
					}
				}
				else /* ONLY LEFT HAND SIDE CAN BE ON STACK */
				{
					data_toevalstack(iv, l, d);
					data_toevalstack(iv, l, d);
				}

				/* ENSURE THAT THE RIGHT SIDE IS A LONG OR A LONG LONG */
				bestptr = (l->size > 4) ? &longlongtype : &longtype;
				if(check_assignment_conversion(bestptr, r))
					do_conversion(iv, bestptr, r);

				if(l->opsize == B8)
				{
					obuf[oc++] = XTD;
				}
				obuf[oc++] = binops[opcode]|get_datasize(opcode, l);
				write_obuf(iv, obuf, oc);
				--iv->stackdepth;
			}/* END: NOT IMMEDIATES */

			if(check_assignment_conversion(d, l))
				do_conversion(iv, d, l);
			l->atype &= ~A_MEMADDR;
			l->atype |= A_VALUE;
			from_evalstack(iv, d, l);

			break;
		}/* END: MODULUS */
		case truthop:
		case aliastmpop:
		{/* no immediates */

			data_toevalstack(iv, l, d);

			if(l->opsize != B1)
			{/* no need to get truth of byte sized elements */
				if(l->opsize == BX)
				{
					obuf[oc++] = XTD;
				}
				obuf[oc++] = TRUTHOF|l->opsize;
				write_obuf(iv, obuf, oc);
			}
			break;
		}
		case complop:
		case notop:
		{/* no immediates */
		unsigned char osize = get_datasize(opcode, l);

			data_toevalstack(iv, l, d);
			if(osize == BX)
			{/* only notop can operate on long double */
				obuf[oc++] = XTD;
			}
			obuf[oc++] = binops[opcode]|osize;
			write_obuf(iv, obuf, oc);
			break;
		}
		case negop:
		{/* no immediates */

			data_toevalstack(iv, l, d);
			if(l->opsize == BX || (l->opsize==B8 && l->dtype != D_FLOAT))
			{
				obuf[oc++] = XTD;
			}
			obuf[oc++] = binops[opcode]|get_datasize(opcode, l);
			write_obuf(iv, obuf, oc);
			break;
		}
		case copyop:
		{
			if(d->atype & (A_TEMP|A_RET))
			{
				if(!(l->atype & (A_TEMP|A_RET)))
				{
					data_toevalstack(iv, l, d);
				}
			}
			else
			{
				if(!(l->atype & (A_TEMP|A_RET)))
				{
					data_toevalstack(iv, d, d);
					data_toevalstack(iv, l, d);
				}
				else
				{
					data_toevalstack(iv, d, d);
					obuf[0] = SWAP4;
					write_obuf(iv, obuf, 1);
				}
			}
			load_val(iv, d->size);
			obuf[0] = MOVAAC;
			write_obuf(iv, obuf, 1);
			iv->stackdepth -= 3;
			break;
		}
		case castop:
		{
			data_toevalstack(iv, l, d);
			if(check_assignment_conversion(d, l))
				do_conversion(iv, d, l);
			break;
		}
		case jmploopop:
		case jmpcontinueop:
		case jmpbreakop:
		case jmpgotoop:
		case ljmptrueop:
		case jmptrueop:
		case ljmpfalseop:
		case jmpfalseop:
		case funcstartop:
		case funcstopop:
		{/* start with max sized jmps, they will be shortened later */
		void *fixjmp;
			if(opcode == jmptrueop)
			{
				obuf[0] = JMPT|J4;
				--iv->stackdepth;
			}
			else if(opcode == ljmptrueop)
			{
				obuf[0] = LJMPT|J4;
			}
			else if(opcode == jmpfalseop)
			{
				obuf[0] = JMPF|J4;
				--iv->stackdepth;
			}
			else if(opcode == ljmpfalseop)
			{
				obuf[0] = LJMPF|J4;
			}
			else if(opcode == funcstartop || opcode == funcstopop)
			{
				obuf[0] = LOCATE|J4;
			}
			else
				obuf[0] = JMP|J4;
			fixjmp = write_obuf(iv, obuf, 5);
			addto_jmplist(iv, p, fixjmp);
			break;
		}
		case clrdatop:
		{
			addr_toevalstack(iv, d);
			data_toevalstack(iv, l, 0);
			obuf[0] = XTD;
			obuf[1] = CLRDAT;
			write_obuf(iv, obuf, 2);
			iv->stackdepth -= 2;
			break;
		}
		case getbitfieldop:
		{
			data_toevalstack(iv, r, d);
			obuf[0] = XTD;
			obuf[1] = GETBITFIELD;
			obuf[2] = l->data->uchr.d[0];	/* shift */
			obuf[3] = l->data->uchr.d[1];	/* size */
			obuf[4] = l->data->uchr.d[2];	/* sign */
			write_obuf(iv, obuf, 5);
			break;
		}
		case putbitfieldop:
		{
			if(!(d->atype & (A_TEMP|A_RET)))
			{/* destination address to stack */
				addr_toevalstack(iv, d);

				if(!(r->atype & (A_TEMP|A_RET)))
				{
					data_toevalstack(iv, r, d);
				}
				else
				{/* data was on stack */
					obuf[0] = SWAP;
					write_obuf(iv, obuf, 1);
				}
			}
			else
			{/* destination address is on stack */
				data_toevalstack(iv, r, d);
			}
			if(check_assignment_conversion(d, r))
				do_conversion(iv, d, r);

			obuf[0] = XTD;
			obuf[1] = PUTBITFIELD;
			obuf[2] = l->data->uchr.d[0];	/* shift */
			obuf[3] = l->data->uchr.d[1];	/* size */
			obuf[4] = d->size;
			write_obuf(iv, obuf, 5);
			iv->stackdepth -= 2;
			break;
		}
		case retstructop:
		{
			addr_toevalstack(iv, d);
			obuf[0] = XTD;
			obuf[1] = RETSTRUCT;
			*((unsigned short*)&obuf[2]) = l->data->ulng.d[0] >>2;	/* size */
			*((unsigned short*)&obuf[4]) = l->data->ulng.d[1] >>2;	/* offset */
			write_obuf(iv, obuf, 6);
			break;
		}
		case retdataop:
		case retvoidop:
		{
			obuf[0] = RET;
			write_obuf(iv, obuf, 1);
			break;
		}
		case callfuncop:
		{
			obuf[0] = XTD;
			obuf[1] = CALLSETUP;
			*((long*)&obuf[2]) = l->data->ulng.d[0];	/* argsize */
			write_obuf(iv, obuf, 6);
			break;
		}
		case switchop:
		{
		void *fixjmp;
			obuf[0] = XTD;
			obuf[1] = SWITCH;
			*((short*)(&obuf[2])) = d->data->ulng.d[1];	/* swnode */
			write_obuf(iv, obuf, 4);

			/* Default jmp */
			obuf[0] = JMP|D4;
			fixjmp = write_obuf(iv, obuf, 5);
			addto_jmplist(iv, p, fixjmp);
			--iv->stackdepth;
			break;
		}
		case compsavop:
		case totmpop:
		{
			data_toevalstack(iv, l, d);
			break;
		}
		case argop:
		{
		unsigned char op =	arg_toevalstack(iv, l, d->ARGSIZE);
			if(!(iv->in_builtin))
			{
				load_val(iv, d->ARGOFS);
				load_val(iv, d->ARGSIZE);
				obuf[0] = op;
				write_obuf(iv, obuf, 1);
				iv->stackdepth -= 3;
			}
			break;
		}
		default:
		{
#if 0
			obuf[0] = NOP;
			write_obuf(iv, obuf, 1);
#endif
			break;
		}
	}/* END: switch(opcode) */
	if(iv->stackdepth > iv->maxdepth)
		iv->maxdepth = iv->stackdepth;
	if(iv->stackdepth < iv->mindepth)
		iv->mindepth = iv->stackdepth;

	return iv->cod_usedtail;
}/* END: gen_inst() */

static void
set_op(PND pnd, PopA ptr, unsigned char otype)
{
unsigned char optype  = otype & 0xe0;
unsigned char isize = otype & 0x1f;

	pnd->data = (DATUM *)ptr;
	if(optype <= OPIMMED4)
	{
		pnd->dtype = optype >> 5;
		pnd->quals = Q_CONST;
		pnd->size = isize;
		pnd->opsize = get_size(pnd->dtype, isize);
		pnd->atype = A_IMMED;
	} /* END: OPIMMED */
	else
	{
	unsigned short type = GS(ptr->dtype);
		pnd->dtype = type & 0xff;
		pnd->quals = (type >> 8) & 0xff;
		pnd->size = GL(ptr->dsize);
		pnd->opsize = get_size(pnd->dtype, pnd->size);
		pnd->atype = GS(ptr->atype);

		if(optype == OPAUTO)
		{
			if(GL(ptr->pofs) >= 0)
			{
				pnd->atype &= ~A_AUTO;
				pnd->atype |= A_PARAM;
			}
		}
	}
}/* END: set_op() */
static void *
decode_anf(Piv iv, unsigned char *p)
{
void *dptr;
void *lptr;
void *rptr;

	if(iv->debug >= '4')
		cfeprintf("DECODE inst(%u) `%s'\n", *p, oxgenops[*p]);

	if(*p == 0)
		return POP->next;


	iv->ob->p = p;
	switch(*p)
	{
		case regainop:
		case grabop:
		case retvoidop:
		{/* 0 address mode */
			break;
		}
		case jmploopop:
		case jmpcontinueop:
		case jmpbreakop:
		case jmpgotoop:
		case ljmptrueop:
		case jmptrueop:
		case ljmpfalseop:
		case jmpfalseop:
		case funcstartop:
		case funcstopop:
		case retdataop:
		{/* 1 address mode */
			dptr = (p+8);
			set_op(&iv->ob->d, dptr, p[1]);
			break;
		}
		case getvalop:
		case derefop:
		case assignop:
		case duptmpop:
		case truthop:
		case aliastmpop:
		case negop:
		case complop:
		case notop:
		case copyop:
		case castop:
		case clrdatop:
		case compsavop:
		case totmpop:
		case retstructop:
		case switchop:
		case argop:
		{/* 2 address mode */
			dptr = (p+8);
			lptr = (((char*)dptr) + (p[1] & 0x1f));
			set_op(&iv->ob->d, dptr, p[1]);
			set_op(&iv->ob->l, lptr, p[2]);
			break;
		}
		case plusop:
		case minusop:
		case mulop:
		case divop:
		case modop:
		case orop:
		case xorop:
		case andop:
		case eqop:
		case neqop:
		case ltop:
		case gtop:
		case leop:
		case geop:
		case lshop:
		case rshop:
		case getbitfieldop:
		case callfuncop:
		case putbitfieldop:
		{/* 3 address mode */
			dptr = (p+8);
			lptr = (((char*)dptr) + (p[1] & 0x1f));
			rptr = (((char*)lptr) + (p[2] & 0x1f));
			set_op(&iv->ob->d, dptr, p[1]);
			set_op(&iv->ob->l, lptr, p[2]);
			set_op(&iv->ob->r, rptr, p[3]);
			break;
		}
		default:
		{
			return POP->next;
		}
	}/* END: switch(*p) */

	/* Save pointers to the machine instructions generated by this ANF code */
	iv->ob->startinst = iv->cod;
	iv->ob->endinst = gen_inst(iv, iv->ob);

	if(iv->cod != iv->ob->startinst)
	{/* instructions were generated */
		link_ob(iv);
	}
	return POP->next;
}/* END: decode_anf() */

static void *
skip_bracket(unsigned char *p)
{
long opcode = *p;
int opcount = 0;

	for(;;)
	{
		if(*p == opcode)
			++opcount;
		else if(*p == endop && GL(POP->data) == opcode)
		{
			if(--opcount == 0) {
				return p;
			}
		}
		else if(*p == endfileop || *p == endallop)
		{
			PERROR(pName ":ERROR: Malformed input file3=%u=%p",*p, p);
		}
		p = POP->next;
	}
	return 0;
}
static void *
do_strelem(Piv iv, unsigned char *p)
{
void *sp = p;
unsigned char *q = skip_bracket(p);
void *elem[20];	
void *elemq[20];
int elemcnt, i;

	elemcnt = 0;
	p = POP->next;
	while(p < q)
	{/* Pick up nested elements */
	unsigned char opcode = *p;
		if(opcode == strelemop || opcode == ptrelemop || opcode == arrayelemop)
		{
		void *qq = skip_bracket(p);
			elem[elemcnt] = p;
			elemq[elemcnt++] = qq;
			p = qq;
		}
		p = POP->next;
	}

	/* Dump the nested elements in the order encountered */
	for(i = 0; i < elemcnt; ++i)
	{
		do_something(iv, elem[i]);
	} 

	/* Dump the remainder of bracket */
	p = sp;
	elemcnt = 0;
	p = POP->next;
 	while(p < q)
	{
	unsigned char opcode = *p;
		if(opcode == strelemop || opcode == ptrelemop || opcode == arrayelemop)
			p = ((Pop)elemq[elemcnt++])->next;
		else
			p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static void *
do_ptrelem(Piv iv, unsigned char *p)
{
void *sp = p;
unsigned char *q = skip_bracket(p);
void *elem[20];	
void *elemq[20];
int elemcnt, i;

	elemcnt = 0;
	p = POP->next;
	while(p < q)
	{/* Pick up nested elements */
	unsigned char opcode = *p;
		if(opcode == arrayelemop || opcode == ptrelemop || opcode == strelemop)
		{
		void *qq = skip_bracket(p);
			elem[elemcnt] = p;
			elemq[elemcnt++] = qq;
			p = qq;
		}
		p = POP->next;
	}

	/* Dump the nested elements in the order encountered */
	for(i = 0; i < elemcnt; ++i)
	{
		do_something(iv, elem[i]);
	} 

	/* Dump the remainder of bracket */
	p = sp;
	elemcnt = 0;
	p = POP->next;
 	while(p < q)
	{
	unsigned char opcode = *p;
		if(opcode == arrayelemop || opcode == strelemop || opcode == ptrelemop)
			p = ((Pop)elemq[elemcnt++])->next;
		else
			p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static void *
do_arrayelem(Piv iv, unsigned char *p)
{/* Arrange output for the stack machine */
void *sp = p;
unsigned char *q = skip_bracket(p);
void *sadims[10];
void *sadimsq[10];
void *spdims[10];	
void *spdimsq[10];
void *elem[20];	
void *elemq[20];
int sacnt, spcnt, elemcnt, i;

	/* Scan over the bracket and pick up the dimension computations */
	elemcnt= sacnt = spcnt = 0;
	p = POP->next;
	while(p < q)
	{
	unsigned char opcode = *p;
		if(opcode == arraydimsop)
		{
		void *qq = skip_bracket(p);
			sadims[sacnt] = p;
			sadimsq[sacnt++] = qq;
			p = qq;
		}
		else if(opcode == ptrdimsop)
		{
		void *qq = skip_bracket(p);
			spdims[spcnt] = p;
			spdimsq[spcnt++] = qq;
			p = qq;
		}
		else if(opcode==arrayelemop || opcode==ptrelemop || opcode==strelemop)
		{
		void *qq = skip_bracket(p);
			elem[elemcnt] = p;
			elemq[elemcnt++] = qq;
			p = qq;
		}
		p = POP->next;
	}

	/* Dump the dimension computations in stack order */
	for(i = spcnt-1; i >= 0; --i)
	{
		do_bracket(iv, spdims[i], spdimsq[i]);
	}
	for(i = sacnt-1; i >= 0; --i)
	{
		do_bracket(iv, sadims[i], sadimsq[i]);
	}

	/* Dump the nested elements in the order encountered */
	for(i = 0; i < elemcnt; ++i)
	{
		do_something(iv, elem[i]);
	} 

	/* Dump the remainder of the bracket */
	p = sp;
	elemcnt = sacnt = spcnt = 0;
	p = POP->next;
 	while(p < q)
	{
	unsigned char opcode = *p;
		if(opcode == arraydimsop)
			p = ((Pop)sadimsq[sacnt++])->next;
		else if(opcode == ptrdimsop)
			p = ((Pop)spdimsq[spcnt++])->next;
		else if(opcode==arrayelemop || opcode==ptrelemop || opcode==strelemop)
			p = ((Pop)elemq[elemcnt++])->next;
		else
			p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static int
funcret_used(PopT op, unsigned char *p)
{
long tmpnum;
	if(		op->dtype == 0
		&&	op->dsize == 0)
	{
		return 1;						/* void function */
	}
	tmpnum = op->tmpnum;

	while(*p != funcexitop)
	{
		if(*p && *p <= (unsigned char)100)
		{
		unsigned char *qp = p+8;
			if(*p == callfuncop)
			{/* function using same temp */
				if(GL(((PopT)qp)->tmpnum) == tmpnum)
				{
					return 1;		/* not used earlier */
				}
			}
			else
			{
				if((p[1]&0xe0) == OPRET)
				{
 					if(GL(((PopT)qp)->tmpnum) == tmpnum)
					{
						return 0;			/* ret used */
					}
				}				
				qp += p[1]&0x1f;
				if((p[2]&0xe0) == OPRET)
				{
					if(GL(((PopT)qp)->tmpnum) == tmpnum)
					{
						return 0;			/* ret used */
					}
				}
				qp += p[2]&0x1f;
				if((p[3]&0xe0) == OPRET)
				{
					if(GL(((PopT)qp)->tmpnum) == tmpnum)
					{
						return 0;			/* ret used */
					}
				}
			}
		}
		p = POP->next;
	}
	return 1;					/* ret not used */
}
static unsigned char
check_for_builtin(Piv iv, unsigned char *p, int flag)
{
short symnum;
long key[2];
unsigned char *result;

	if(flag == 0)
	{
		symnum = GS(((PopA)(p+20))->symnum);
	}
	else if(flag == 1)
	{
		symnum = GS(((PopI)(p+16))->s.symnum);
	}
	else if(flag == 2)
	{
		symnum = GS(POPI->s.symnum);
	}
	else return 0;

	key[0] = symnum;
	key[1] = 0;
	if(SymFind(iv->builtintbl, key, &result))
		return *result;
	return 0;
}
static void *
do_funcall(Piv iv, unsigned char *p)
{/* Arrange output for the stack machine */
char obuf[4];
void *sp = p;
unsigned char *q = skip_bracket(p);
int argcnt, i, dump;
void *argloads[100];
void *argloadsq[100];
char *callop = 0;
unsigned char builtin = 0;

	/* Scan over the bracket and pick up argument loads */
	argcnt = 0;
	p = POP->next;
	while(p < q)
	{
		if(*p == getvalop && callop == 0)
		{
			builtin = check_for_builtin(iv, p, 0);
		}
		else if(*p == callfuncop)
			callop = p;
		else if(*p == argloadop)
		{
		void *qq = skip_bracket(p);
			argloads[argcnt] = p;
			argloadsq[argcnt++] = qq;
			p = qq;
		}
		p = POP->next;
	}

	/* Check out whether the function return is used */
	dump = funcret_used((PopT)(callop+8), q);

	/* Calling an interpreter builtin ?? */
	if(builtin)
	{
		iv->in_builtin = 1;

		/* Dump the argument loads in order */
		/* The ARG instruction will be suppressed because iv->in_builtin > 0 */
		for(i = 0; i < argcnt; ++i)
		{
			do_bracket(iv, argloads[i], argloadsq[i]);
		}
		iv->stackdepth -= argcnt;

		/* Generate the BUILTIN instruction */
		i = 3;
		++iv->stackdepth;
		obuf[0] = XTD;
		obuf[1] = BUILTIN;
		obuf[2] = builtin;
		if(dump)
		{
			obuf[3] = DUMP;
			--iv->stackdepth;
			i = 4;
		}
		write_obuf(iv, obuf, i);
		iv->in_builtin = 0;
	}
	else
	{
		/* Generate the CALLSETUP instruction */
		p = sp;
		p = POP->next;
		while(p < q)
		{
			if(*p == argloadop)
				break;
			else
				p = do_something(iv, p);
		}			

		/* Dump the argument loads in order */
		for(i = 0; i < argcnt; ++i)
		{
			do_bracket(iv, argloads[i], argloadsq[i]);
		}
		/* Generate the CALL instruction */
		i = 1;
		obuf[0] = CALL;
		if(dump)
		{
			obuf[1] = DUMP;
			--iv->stackdepth;
			i = 2;
		}
		write_obuf(iv, obuf, i);
		if((GL(((Pop)(callop))->data1) & 0xff) == D_STRUCT)
		{
			++iv->has_structret;
		}
	}
	return ((Pop)q)->next;
}
static void *
do_expr(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	if(iv->debug >= '2')
		cfeprintf("EXPR inst(%u) `%s'\n", *p, oxgenops[*p]);

	while(p < q)
	{
		switch(*p)
		{
			case	arrayelemop:
				p = do_arrayelem(iv, p);
				break;
			case	strelemop:
				p = do_strelem(iv, p);
				break;
			case	ptrelemop:
				p = do_ptrelem(iv, p);
				break;
			case	funcallop:
				p = do_funcall(iv, p);
				break;
			case	condop:
			case	twopathop:
			case	logicalop:
			case	binopop:
			case	argloadop:
			case	preincrdecop:
			case	postincrdecop:
			case	compoundop:
			case	unopop:
				p = POP->next;
				break;
			default:
				p = do_something(iv, p);
				break;
		}
	}
	return ((Pop)q)->next;
}
static void *
do_expstmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
	return q;
}
static void *
do_ifstmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}

	return ((Pop)q)->next;
}
static void *
do_ifelsestmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}

	return ((Pop)q)->next;
}
static void *
do_switchstmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}

	return ((Pop)q)->next;
}
static void *
do_whilestmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static void *
do_dostmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static void *
do_forstmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static void *
do_asmstmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static void *
do_initstmt(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static void *
do_anfblock(Piv iv, unsigned char *p)
{
unsigned char *q = skip_bracket(p);

	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
	return ((Pop)q)->next;
}
static char *
nodot(Piv iv, char *name)
{
char *cp;
	if((cp = strchr(name, '.')))
	{
	char *newname;
		newname = Cmalloc(iv->category, strlen(name)+1);
		strcpy(newname,name);
		cp = strchr(newname, '.');
		*cp = 0;
		return newname;
	}
	return name;
}
static void
printfunc(Piv iv, unsigned char *p)
{
char *funcname = nodot(iv, iv->symaddr[GS(POPI->funcdef.symnum)]);

	if(*p == gfuncdefop)
	{
		fprintf(iv->outfile, "\n%8.8lx:    .global .function _%s\n",
			iv->out_offset, funcname);
	}
	else if(*p == sfuncdefop)
	{
		fprintf(iv->outfile, "\n%8.8lx:    .local .function _%s\n", 
			iv->out_offset, funcname);
	}
}
static void *
do_stmt(Piv iv, unsigned char *p)
{
void *q;

	if(iv->debug >= '2')
		cfeprintf("STMT inst(%u) `%s'\n", *p, oxgenops[*p]);

	q = POP->next;

	switch(*p)
	{
		case labelop:
			newlabel_insert(iv, GL( POP->data));
			break;
		case gfuncdefop:
		case sfuncdefop:
		case funcexitop:
			PERROR(pName ":ERROR: Malformed input file1=%u=%p",*p, p);
		case nestedfuncdefop:
		{
			if(iv->listing_wanted)
			{
			char obuf[8];
				obuf[0] = NFUNC;
				*((char**)&obuf[1]) = nodot(iv, iv->symaddr[GS(POPI->funcdef.symnum)]);
				write_obuf(iv, obuf, 5);
			}
			iv->numnested += 1;
			break;
		}
		case nestedfuncexitop:
		{
		char obuf[2];
			obuf[0] = RET;
			write_obuf(iv, obuf, 1);
			break;		
		}
		case anfblockop:
			q = do_anfblock(iv, p);
			break;
		case expstmtop:
			q = do_expstmt(iv, p);
			break;
		case ifstmtop:
			q = do_ifstmt(iv, p);
			break;
		case ifelsestmtop:
			q = do_ifelsestmt(iv, p);
			break;
		case switchstmtop:
			q = do_switchstmt(iv, p);
			break;
		case whilestmtop:
			q = do_whilestmt(iv, p);
			break;
		case dostmtop:
			q = do_dostmt(iv, p);
			break;
		case forstmtop:
			q = do_forstmt(iv, p);
			break;
		case asmstmtop:
			q = do_asmstmt(iv, p);
			break;
		case initstmtop:
			q = do_initstmt(iv, p);
			break;
		case lineop:
			if(iv->debug >= '3')
			{
				cfeprintf("Line=%u depth=%d\n", GL(POP->data), iv->stackdepth);
			}
			if(iv->listing_wanted)
			{
			char obuf[8];
				obuf[0] = LINENO;
				*((long*)&obuf[1]) = GL(POP->data);
				write_obuf(iv, obuf, 5);
			}
			iv->lastline = GL(POP->data);
			break;
		default:
			break;
	}
	return q;
}
static void *
do_something(Piv iv, unsigned char *p)
{
	if(*p < labelop)
		return decode_anf(iv, p);
	else if(*p >= condop && *p < symbop)
		return do_expr(iv, p);
	else
		return do_stmt(iv, p);
}
static void
do_bracket(Piv iv, unsigned char *p, unsigned char *q)
{
	p = POP->next;
	while(p < q)
	{
	  p = do_something(iv, p);
	}
}
static void *
dumpa_func(Piv iv, unsigned char *p)
{
unsigned char obuf[2];
Pafile pf;
unsigned char *pdef = p;

	iv->ob = new_nodeO(iv);		/* setup first intermediate output node */
	link_ob(iv);				/* null node never unlinked */
	iv->first_ob = iv->ob_usedtail;

	iv->cod = new_nodeC(iv);	/* setup first code node */
	link_cod(iv);				/* null node never unlinked */
	iv->first_cod = iv->cod_usedtail;

	pf = iv->files[iv->filenum];
	if(iv->listing_wanted)
	{
		printfunc(iv, p);
	}
	p = POP->next;

	for(;;)
	{
		if(*p == funcexitop)
		{
			obuf[0] = RET;
			write_obuf(iv, obuf, 1);
			write_funcdata(iv, pdef);
			break;
		}
		else
			p = do_something(iv, p);
	}
	save_extlocs(iv);
	reset_funcdata(iv);
	return p;
}
static void
dump_funcs(Piv iv)
{
long pad;
Pafile pf;
unsigned char *p;
int i;

#if 0
oxcc_debug(__builtin_iv(),0x40040);
#endif

	reset_funcdata(iv);
	for(i = 0; i < iv->numfiles; ++i)
	{
		iv->filenum = i;
		pf = iv->files[i];
		p = pf->file_p;
		if(iv->listing_wanted)
			fprintf(iv->outfile, "\n\nFile:%s:\n\n", pf->symaddr[1]);

		while(*p != endfileop)
		{
			if(*p == labelop)
			{
				newlabel_insert(iv, GL( POP->data ));
			}
			else if(*p == lineop)
			{
				iv->lastline = GL(POP->data);
				if(iv->debug >= '3')
				{
					cfeprintf("Line=%u depth=%d\n", iv->lastline, iv->stackdepth);
				}
				if(iv->listing_wanted)
					fprintf(iv->outfile, "Line:%ld:\n", iv->lastline);
			}
			else if(*p == gfuncdefop || *p == sfuncdefop)
			{
				p = dumpa_func(iv, p);
			}
			else if(*p == anfblockop)
			{
				PERROR(pName ": Sorry, Outer anf blocks not handled.\n");
			}
			p = POP->next;;
		}
	}

	/* Start the data area on an 8 byte boundary */
	if((pad = iv->out_offset & 7))
		pad = 8-pad;
	iv->out_offset += pad;

	if(pad && !iv->listing_wanted)
		FILEWRITE(padit, pad);
	iv->header->a_text = iv->out_offset;
}
static void
fix_thunks(Piv iv)
{
Pafile pf;
int i;
unsigned char **vp;
unsigned char *p;

	for(i = 0; i < iv->numfiles; ++i)
	{
		iv->filenum = i;
		pf = iv->files[i];
		if(SymHead(pf->datatbl))
		{
			while(SymNext(pf->datatbl))
			{
				SymValue(pf->datatbl, &vp);
				p = vp[1];
				if(*p == thunkblockop)
				{/* rearrange the FUNCTHUNK */
				unsigned short mods;
					mods = GS(POP->data7);
					mods &= 0xc000;
					mods |= GS(POP->data9) & 0x1f1f;
					PS( POP->data7 ) = mods;
					PS( POP->data9) = get_maxdepth(iv, GS( POP->data2 ));
					if(mods & Fextern)
					{/* Put symbol number in the offset slot */
						PL( POP->data5 ) = final_symnum(iv, GS( POP->data2 ));
					}
					else
					{/* Put function offset in the offset slot */
						PL( POP->data5 ) = newlabel_fix(iv, GL( POP->data5 ));
					}
				}
			}
		}
	}
}
static void
printdata(Piv iv, char *sym, char *msg, void *ptr, int size, 
	long offset, int locid)
{
int x;

	x = print8(iv, ptr, size, offset, 0);
	if(locid > 0)
		fprintf(iv->outfile,"  _%s.%d (%s)\n", sym, locid, msg);
	else
		fprintf(iv->outfile,"  _%s (%s)\n", sym, msg);
	while((size -= x) > 0)
	{
		offset += x;
		((char*)ptr) += x;
		x = print8(iv, ptr, size, offset, 1);
	}
}
static void
printbss(Piv iv, char *sym, int size, int offset, unsigned char prevopcode,
								int locid)
{
	if(prevopcode == globssop)
	  fprintf(iv->outfile, "%8.8x:  _%s (BSS %d)\n", offset, sym, size);
	else
	  fprintf(iv->outfile, "%8.8x:  _%s.%d (bss %d)\n", offset, sym, locid, size);
}
static void
dump_data(Piv iv)
{
struct _val {
unsigned long size;
unsigned char *p;
unsigned char *prevp;
long locid;
};
struct _val *val;
unsigned long *key;
long datsize = 0;
long bsssize = 0;
long curr_offset = iv->out_offset;
unsigned char opcode, prevopcode = 0;

	if(SymHead(iv->datatbl))
	{
	void *savaddr;
		if(iv->listing_wanted)
		{
			fprintf(iv->outfile, "\n\t.data\n\n");
			savaddr = iv->symaddr[0];
			iv->symaddr[0] = "STRING_";
		}
		while(SymNext(iv->datatbl))
		{
		unsigned char *p;
		long size;
		long pad;
			SymKey(iv->datatbl, &key);
			SymValue(iv->datatbl, &val);
			p = val->p;
			opcode = *p;
			if(val->prevp)
				prevopcode = *(val->prevp);
			size = val->size;
			if((pad = size & 3))  /* the interpreter requires 4 byte alignment */
				pad = 4-pad;

			if(		opcode == datablockop
				||	opcode == mallocblockop
				||  opcode == thunkblockop
				||	opcode == stringblockop)
			{
				if(iv->listing_wanted)
				{
				char *msg;

					if(		opcode == datablockop
						||	opcode == stringblockop)
					{
						if(prevopcode == glodatop)
							msg = "DATA";
						else
						{
							msg = "data";
						}
					}
					else if(opcode == mallocblockop)
					{
						msg = "alloced";
					}
					else /* thunkblockop */
					{
						if(prevopcode == extfuncop)
						{
						long bu;
							if((bu = check_for_builtin(iv, p, 2)))
							{
							Pft ft = (Pft)(p+24);
								ft->funcaddr = bu;
								ft->fmods |= Fbuiltin;
								ft->fmods &= ~Fextern;
								*(val->prevp) = 0xff;
								msg = "thunk";
							}
							else
								msg = "EXTHUNK";
						}
						else if(prevopcode == glofuncop)
							msg = "THUNK";
						else
						{
							msg = "thunk";
						}
					}
					printdata(iv, iv->symaddr[GS(POP->data2)], 
							msg, p+24, size, GL(POP->data1), val->locid);
				}
				else
				{
					if(opcode == thunkblockop)
					{
						if(prevopcode == extfuncop)
						{
						long bu;
							if((bu = check_for_builtin(iv, p, 2)))
							{
							Pft ft = (Pft)(p+24);
								ft->funcaddr = bu;
								ft->fmods |= Fbuiltin;
								ft->fmods &= ~Fextern;
								*(val->prevp) = 0xff;
							}
						}
					}
					FILEWRITE(p+24, size);
					if(pad > 0)
						FILEWRITE(padit, pad);
				}
				datsize += size+pad;
				iv->out_offset += size+pad;
			}
			else if(opcode == bssblockop)
			{
				if(iv->listing_wanted)
				{
					printbss(iv, iv->symaddr[GS(POP->data2)], size, 
						GL(POP->data1), prevopcode, val->locid);
				}
				bsssize += size+pad;
			}
		}
		if(iv->listing_wanted)
		{
			iv->symaddr[0] = savaddr;
		}
	}
	iv->header->a_data = iv->out_offset - curr_offset;
	if(datsize != iv->header->a_data)
		PERROR(pName,":SYSERROR: data size incorrect (%d)!=(%d)\n",
			datsize, iv->header->a_data);
}
static void
dump_bss(Piv iv)
{
	iv->header->a_bss = iv->total_size - iv->bss_offset;
}
static void
make_final_string_pack(Piv iv)
{
long strsize = 0;
int strcnt = 0;
char *pack;
char *cp;
struct {
	char *str;
	long symnum;
	long symofs;
} *val;

	if(SymHead(iv->finalsymtbl))
	{
		while(SymNext(iv->finalsymtbl))
		{
			SymValue(iv->finalsymtbl, &val);
			++strcnt;
			val->symofs = strsize+4;
			strsize += strlen(val->str)+2;
		}
	}
	pack = Ccalloc(iv->category, 1, strsize+4);
	*((long*)pack) = strsize + 4;
	cp = pack + 4;
	if(SymHead(iv->finalsymtbl))
	{
		while(SymNext(iv->finalsymtbl))
		{
			SymValue(iv->finalsymtbl, &val);
			*cp++ = '_';
			strcpy(cp, val->str);
			cp += strlen(val->str)+1;
		}
	}
	iv->finalstringpack = pack;
	iv->finalpacksize = strsize+4;
}
static unsigned char *
dump_switch(Piv iv, unsigned char *p, int filenum)
{
struct nlist nl;
unsigned char *q = skip_bracket(p);

	nl.n_type = N_SWTAB;
	nl.n_other = 0;
	while(p < q)
	{
		if(*p == switchidop)
		{
			nl.n_desc = GS(POP->data1);	/* id */
		}
		else if(*p == casevalop)
		{
		long key[2];
		long *result;
			nl.n_un.n_strx = GL(POP->data1);	/* value */
			key[0] = GL(POP->data); /* label number */
			key[1] = filenum;
			if(SymFind(iv->newlabeltbl, &key, &result))
			{
				nl.n_value = *result;			/* offset */
			}
			else
			{
				PERROR(pName ":SYSERROR: case label not found\n");
			}
			if(iv->listing_wanted)
			{
				/* print nothing */
			}
			else
			{
				FILEWRITE(&nl, sizeof(struct nlist));
				iv->out_offset += sizeof(struct nlist);
			}
		}
		p = POP->next;
	}
	return ((Pop)q)->next;
}
static void
dump_symbols(Piv iv)
{
long curr_offset = iv->out_offset;
int i;
struct nlist nl;

 	nl.n_other = 0;
	nl.n_desc = 0;

	make_final_string_pack(iv);

	if(SymHead(iv->gbltbl))
	{
		if(iv->listing_wanted)
		{
			fprintf(iv->outfile, "\n\t.symbols\n\n");
		}
		while(SymNext(iv->gbltbl))
		{
		struct _gloval *valp;
		unsigned char opcode;

			SymValue(iv->gbltbl, &valp);
			if((opcode = *(valp->p)))
			{
			PopI pp;
				pp = (PopI) (((Pop)(valp->p))->next+8);
				nl.n_un.n_strx = final_strofs(iv, valp->symname);
				if(opcode == glodatop)
				{
					nl.n_type = N_DATA|N_EXT;
					nl.n_value = pp->s.offset + iv->header->a_text;
				}
				else if(opcode == globssop)
				{
					nl.n_type = N_BSS|N_EXT;
					nl.n_value = pp->s.offset + iv->header->a_text;
				}
				else if(opcode == glofuncop || opcode == extfuncop)
				{/* The symbol is really a thunk in the data section */
					nl.n_type = N_NDC|N_EXT;
					nl.n_value = pp->s.offset;
				}
				else if(opcode == extvarop)
				{
					nl.n_type = N_UNDF|N_EXT;
					nl.n_value = 0;
				}
				else if(opcode == 0xff)
				{/* Builtin thunk */
					continue;
				}
				if(iv->listing_wanted)
				{
				int symval = nl.n_value;
					if(nl.n_type & (N_DATA|N_BSS))
						symval -= iv->header->a_text;
					fprintf(iv->outfile, "%8.8x:  _%s\n", symval, valp->symname);
				}
				else
				{
					FILEWRITE(&nl, sizeof(struct nlist));
				}
				iv->out_offset += sizeof(struct nlist);
			}
		}
	}
	/* DUMP SWITCH TABLE INFO */
	for(i = 0; i < iv->numfiles; ++i)
	{
	Pafile pf = iv->files[i];
	unsigned char *p;
		if((p = pf->switch_p))
		{
			while(*p != endfileop)
			{
				if(*p == switchidop)
				{
					p = dump_switch(iv, p, i);
				}
				else p = POP->next;
			}
		}
	}
	iv->header->a_syms = iv->out_offset - curr_offset;;
}/* END: dump_symbols() */

static void
dump_text_relocs(Piv iv)
{
long curr_offset = iv->out_offset;
PEL pel = iv->finextbufstart;
struct relocation_info r;

	r.r_pcrel = 0;
	r.r_extern = 1;
	r.r_length = 2;
	r.r_pad = 0;
	if(pel && iv->listing_wanted)
	{
		fprintf(iv->outfile,"\n\t.textrels\n\n");
	}
	while(pel)
	{
		r.r_symbolnum = final_symnum(iv, pel->symnum);
		r.r_address = pel->spot;
		if(pel->symnum > 0)
		{
			if(iv->listing_wanted)
			{
				fprintf(iv->outfile,"%8.8lx: .extern _%s\n", 
					r.r_address, iv->symaddr[pel->symnum]);
			}
			else
			{
				FILEWRITE(&r, sizeof(struct relocation_info));
			}
			iv->out_offset += sizeof(struct relocation_info);
		}
		pel = pel->next;
	}
	iv->header->a_trsize = iv->out_offset - curr_offset;

}/* END: dump_text_relocs() */

static void
dump_data_relocs(Piv iv)
{
long curr_offset = iv->out_offset;
struct relocation_info r;

	r.r_pcrel = 0;
	r.r_length = 2;
	r.r_pad = 0;
	if(SymHead(iv->reloctbl))
	{
		if(iv->listing_wanted)
		{
			fprintf(iv->outfile,"\n\t.datarels\n\n");
		}
		while(SymNext(iv->reloctbl))
		{
		struct _rkey *kp;
		struct _rval *vp;
		unsigned char *p;
		int symnum;
		  SymKey(iv->reloctbl, &kp);
		  SymValue(iv->reloctbl, &vp);
		  p = vp->p;		/* pointer to relocop in input buffer */
		  symnum = GS(POPI->reloc.rsym);

		  if(kp->rsize == 1)
		  	r.r_length = 0;
		  else if(kp->rsize == 2)
		  	r.r_length = 1;
		  else if(kp->rsize == 4)
		  	r.r_length = 2;
		  else PERROR(pName ":ERROR: reloc size too large\n");

		  r.r_address = GL( POPI->reloc.spot );
		  if(*p == extlocop)
		  {/* External variable */
			r.r_extern = 1;
			r.r_symbolnum = final_symnum(iv, symnum);
		  }
		  else if(*p)
		  {
			r.r_extern = 0;
			r.r_symbolnum = N_DATA;
		  }
		  if(*p)
		  {
			  if(iv->listing_wanted)
			  {
				if(r.r_extern)
				{
					if(vp->rsym <= 0)
					{/* an absolute value */
						continue;
					}
					fprintf(iv->outfile,"%8.8lx: .extern _%s\n",
						r.r_address, iv->symaddr[symnum]);
				}
				else
				{
					fprintf(iv->outfile, "%8.8lx: .segrel\n", r.r_address);
				}
			  }
			  else
			  {
				  if(r.r_extern && vp->rsym <= 0)
				  {/* an absolute value */
					continue;
				  }
				  FILEWRITE(&r, sizeof(struct relocation_info));
			  }
			  iv->out_offset += sizeof(struct relocation_info);
			}
		}
	}
	iv->header->a_drsize = iv->out_offset - curr_offset;

}/* END: dump_data_relocs() */

static void
prepare_data_relocs(Piv iv)
{
	if(SymHead(iv->reloctbl))
	{
		while(SymNext(iv->reloctbl))
		{
		struct _rkey *kp;
		struct _rval *vp;
		unsigned char *p;
		  SymKey(iv->reloctbl, &kp);
		  SymValue(iv->reloctbl, &vp);
		  p = vp->p;		/* pointer to relocop in input buffer */

		  if(*p == extlocop)
		  {/* External variable */
		  }
		  else if(*p)
		  {/* a.out format requires a flat address space for relocations */
			if(kp->rsize == 1)
			{
				*((char*)vp->base) += iv->header->a_text;
			}
			else if(kp->rsize == 2)
			{
				*((short*)vp->base) += iv->header->a_text;
			}
			else if(kp->rsize == 4)
			{
				*(vp->base) += iv->header->a_text;
			}
			else PERROR(pName ":ERROR: reloc size too large\n");

		  }
		}
	}
}/* END: prepare_data_relocs() */

static void
dump_symbol_strings(Piv iv)
{
	if(iv->listing_wanted)
	{
		/* print nothing */
	}
	else
	{
		FILEWRITE(iv->finalstringpack, iv->finalpacksize);
	}
}/* END: dump_symbol_strings() */

static int
gen_output(Piv iv, char *outpath)
{/* Bytecode output */
char *cp;
int i;
char outname[256];
	
	strcpy(outname, outpath);
	if((cp = strrchr(outname, '.')))
	{
#if 0
	  if(iv->listing_wanted)
		strcpy(cp, ".lst");
	  else
		strcpy(cp, ".byt");
#endif
	}
	else
	{
	  if(iv->listing_wanted)
		strcat(outname, ".lst");
	  else
		strcat(outname, ".byt");
	}
	for(i = 1; i < iv->argc; ++i)
	{
	  if(!strcmp(outname, iv->argv[i]))
	  {
		PERROR(pName ":ERROR: output file `%s' is same as input file\n", outname);
	  }
	}
	if(!(iv->outfile = fopen(outname, "wb")))
	{
		PERROR(pName ":ERROR: Cannot open output file %s\n", outname);
	}

	/* Allocate a header struct */
	iv->header = Ccalloc(iv->category, 1, sizeof(struct exec));

	if(iv->listing_wanted)
	{
	long tim = time(0);
	char *date = ctime(&tim);
		fprintf(iv->outfile,"/*\n  `%s'     %s", outname, date);
		fprintf(iv->outfile,notice,MAJOR_VERSION,MINOR_VERSION);
	}
	else
	{/* Seek past the header area */
		iv->header->a_info = OMAGIC;
		fseek(iv->outfile, sizeof(struct exec), SEEK_SET);
	}
	install_builtins(iv);
	make_final_symtab(iv);

#if 0
if(iv->debug)
bterpdebug();
#endif
	dump_funcs(iv);
	fix_thunks(iv);
	prepare_data_relocs(iv);	/* adjust addresses for flat space */
	dump_data(iv);
	dump_bss(iv);
	dump_text_relocs(iv);
	dump_data_relocs(iv);
	dump_symbols(iv);
	dump_symbol_strings(iv);

	/* Update the header block */
	if(!iv->listing_wanted)
	{
		fseek(iv->outfile, 0, SEEK_SET);
		FILEWRITE(iv->header, sizeof(struct exec));
	}
	fclose(iv->outfile);
	iv->outfile = 0;	
	return iv->errors;
}
/* ======================= END BYTECODE OUTPUT GENERATOR ==================== */

/* ===================== GENERIC CODE BELOW THIS POINT ================== */
static jmp_buf run_env;
static void
prerror(const char *fmt, ...)
{
	VFPRINTF(fmt, (char *)(((int *)(&fmt))+1));
	longjmp(run_env, 3);
}
static void
prwarn(const char *fmt, ...)
{
	VFPRINTF(fmt, (char *)(((int *)(&fmt))+1));
}
static void
info(const char *fmt, ...)
{
	vfprintf(stdout, fmt, (char *)(((int *)(&fmt))+1));
}
/* ========================= MULTI HEAP MALLOC ========================== */
#define LOCAL static

#if USING_FRAMEWORK
#define THEWELL(a) mallocC(local_category, a)
static int local_category;
static int num_instance;
extern void *mallocC(int, int);
extern void freecat(int);
extern void oxlink_clear_bss();
extern int NewMallocCategory();
#endif /* USING_FRAMEWORK */

#define BASE_CATEGORY 0
#define MEMORY_BUG 0
#define PRINT_RAWDATA 0

#if MEMORY_BUG == 1
#define MPRINTF printf
#else
#define MPRINTF(args...)
#endif

#define PAGESIZE (4096)	/* can use `pagesize' function in OS */
#define ALIGNMENTM (sizeof(unsigned long))
#define MAL_MAXLEVEL (12)
#define ROUNDINGM(a) ((a+(ALIGNMENTM-1))&~(ALIGNMENTM-1))
#define ALLOCSIZE (4096)
#define FRNTGUARD (0x544e5246UL)
#define BACKGUARD (0x48434142UL)
#ifndef THEWELL
#define THEWELL do_sbrk
#endif

#define NUMTYPES 3
#define SIZEH 0
#define FREEH 1
#define USEDH 2

#define SKIPVARS NodePM update[MAL_MAXLEVEL+1];NodePM node,prev;int level

#define DELETENODE(TYPE) \
{for(level=0;level<=bp->TYPE##level; level++)\
{if(update[level]->fptr[level] == node)\
update[level]->fptr[level] = node->fptr[level];else break;}\
while(bp->TYPE##level>0 && bp->TYPE##header->fptr[bp->TYPE##level]==_NILLL)\
bp->TYPE##level--;free_Mnode(bp,node,TYPE);}

#define INSERT() \
{while(level >= 0){\
node->fptr[level] = update[level]->fptr[level];\
update[level]->fptr[level] = node;level--;}}

#define SETLEVEL(TYPE) \
{level = getMlevel(bp, bp->TYPE##level);\
while(bp->TYPE##level < level)update[++bp->TYPE##level]=bp->TYPE##header;}

#define FINDKEY(TYPE, KEYVAL)\
{node = bp->TYPE##header;\
for(level = bp->TYPE##level; level >= 0; level--){\
while(node->fptr[level]->key < KEYVAL)\
node = node->fptr[level];\
update[level] = node;}prev=node;node=node->fptr[0];}

#define DETACH(SN)\
{SN->bptr->fptr=SN->fptr;if(SN->fptr)SN->fptr->bptr=SN->bptr;}

#define UNLINK(SN, N)\
{if(!sp->fptr&&sp->bptr->bptr<=(AddrP)(MAL_MAXLEVEL+1))dsize[N]=sp->size;\
DETACH(SN);free_addr(bp,SN);}

#define CHECKGUARDS(MSG)\
{if(bp->guarded){\
unsigned *p2;\
p2 = (void*)((char*)address+cursize-ALIGNMENTM);\
if(*address != FRNTGUARD)\
PERROR(pName #MSG ":%d: corrupted at 0x%x\n", bp->bincat, addr);\
if(*p2 != BACKGUARD)\
PERROR(pName #MSG ":%d: corrupted by 0x%x\n", bp->bincat, addr);}}

#if MEMORY_BUG == 1
#define HEAPCHECK \
{void *lastaddr;\
if(category > 0){\
guardC(category);\
if((lastaddr = heapcheckC(category, NULL))){\
FINDKEY(USEDH, (unsigned)lastaddr-ALIGNMENTM)\
MPRINTF("bad heap at %x c:%u size=%u\n", lastaddr, category, node->value);\
(void)print_rawdata(lastaddr-ALIGNMENTM, node->value);\
abort();}}}
#else
#define HEAPCHECK
#endif

struct _catlocs {
	void *addr;
	struct _catlocs *fptr;
};

typedef struct _nodeM
{
	unsigned key;
	unsigned value;
	unsigned levels;	/* must always be after value */
	struct _nodeM *fptr[1];
} NodeM, *NodePM;

typedef struct _addr
{
	struct _addr *fptr;
	struct _addr *bptr;
	NodePM maddr;
	unsigned size;
} *AddrP;

struct _bins {
	unsigned bits;
	unsigned nbits;
	NodePM SIZEHheader;
	int SIZEHlevel;
	NodePM FREEHheader;
	int FREEHlevel; 
	NodePM USEDHheader;
	int USEDHlevel;

	unsigned bincat;
	unsigned maxloc;
	unsigned minloc;
	struct _catlocs *catlocs;
	struct _bins *fptr;
	NodePM freenodes[NUMTYPES][MAL_MAXLEVEL+2];
	struct _addr *freeaddrlocs;
	char *chunkbase[NUMTYPES];
	int chunksize[NUMTYPES];
	int guarded;
	int addrbump;
};

static struct _bins zbp;
static struct _bins *hmap[1009];
static struct _nodeM _nilll = {0xffffffff,0,0,{0}};
static struct _nodeM *_NILLL = &_nilll;
static unsigned maxloc;
static unsigned minloc;
static struct _bins *freebinlocs;
static struct _catlocs *freecatlocs;
static char *binbase;
static int binsize;
static int chunksizes[] = {ALLOCSIZE,3*ALLOCSIZE,2*ALLOCSIZE};


static long randtbl[32]	= { 0L,
	0x9a319039L, 0x32d9c024L, 0x9b663182L, 0x5da1f342L, 
	0xde3b81e0L, 0xdf0a6fb5L, 0xf103bc02L, 0x48f340fbL, 
	0x7449e56bL, 0xbeb1dbb0L, 0xab5c5918L, 0x946554fdL, 
	0x8c2e680fL, 0xeb3d799fL, 0xb11ee0b7L, 0x2d436b86L, 
	0xda672e2aL, 0x1588ca88L, 0xe369735dL, 0x904f35f7L, 
	0xd7158fd6L, 0x6fa6f051L, 0x616e6b96L, 0xac94efdcL, 
	0x36413f93L, 0xc622c298L, 0xf5a42ab8L, 0x8a88d77bL, 
	0xf5ad9d0eL, 0x8999220bL, 0x27fb47b9L
};

static  long *fptr	= &randtbl[4];
static  long *rptr	= &randtbl[1];

/* ======================== START OF CODE =========================== */
#if PRINT_RAWDATA == 1
static char
hexbyte(unsigned int c)
{
char x = c & 0xf;

	return x + ((x>9) ? 55 : 48);
}
static void
print_rawdata(void *rawdata, long size)
{
unsigned long vaddr = 0;
unsigned char *d = rawdata;
int i,j;
char addr[9];
char hex1[24];
char hex2[24];
char side1[9];
char side2[9];

	addr[8] = 0;
	hex1[23] = 0;
	hex2[23] = 0;
	side1[8] = 0;
	side2[8] = 0;
	while(size > 0)
	{
	unsigned long qaddr = vaddr;
		memset(addr, '0', 8);
		memset(hex1, ' ', 23);
		memset(hex2, ' ', 23);
		memset(side1, ' ', 8);
		memset(side2, ' ', 8);
		i = 7;
		while(qaddr)
		{
			addr[i--] = hexbyte(qaddr);
			qaddr >>= 4;
		}
		for(i=0,j=0; i < 8; ++i)
		{
			if(--size >= 0)
			{
			unsigned int c = *d++;
				if(isprint(c))
					side1[i] = c;
				else
					side1[i] = '.';
				hex1[j++] = hexbyte(c>>4);
				hex1[j++] = hexbyte(c);
					++j;
			}
			else break;
		}
		for(i=0,j=0; i < 8; ++i)
		{
			if(--size >= 0)
			{
			unsigned int c = *d++;
				if(isprint(c))
					side2[i] = c;					
				else
					side2[i] = '.';
				hex2[j++] = hexbyte(c>>4);
				hex2[j++] = hexbyte(c);
				++j;
			}
			else break;
		}
		VPRINTF("%s  %s%s%s  %s%s%s\n",addr,hex1," | ",hex2,side1,"|",side2);
		vaddr += 16;
	}
}
#endif

/*
 * Returns a really good 31-bit random number.
 */
static long
lrandom()
{
long i;
	
	*fptr += *rptr;
	i = (*fptr >> 1) & 0x7fffffffUL;
	if(++fptr > &randtbl[31])
	{
		fptr = &randtbl[1];
		++rptr;
	}
	else
	{
		if(++rptr > &randtbl[31])  
			rptr = &randtbl[1];
	}
	return( i );
}
#if !USING_FRAMEWORK
static void *
do_sbrk(unsigned amount)
{
void *address;

	address = sbrk(amount);	/* OR WHATEVER TO ACCESS THE OPERATING SYSTEM */
	if(address == (void*)-1)
	{
		PERROR(pName "\nsystem out of memory, requested %u bytes\n", amount);
	}
	return address;
}
#endif
static struct _catlocs *
new_catloc(void)
{
struct _catlocs *p;
	if((p=freecatlocs))
	{
		freecatlocs = p->fptr;
		return p;
	}
	if(binsize < sizeof(struct _catlocs))
	{
		binbase = THEWELL(4096);
		binsize = 4096;
	}
	binsize -= sizeof(struct _catlocs);
	p = (void*)binbase;
	binbase += sizeof(struct _catlocs);
	return p;
}
static void
free_catloc(struct _catlocs *p)
{
	p->fptr = freecatlocs;
	freecatlocs = p;
}
static void *
new_chunk(struct _bins *bp, int size, int type)
{
char *p;
 	if(bp->chunksize[type] < size)
	{
		if(bp->bincat == 0) {
			bp->chunkbase[type] = THEWELL(chunksizes[type]);
			bp->chunksize[type] = chunksizes[type];
		}
		else {
		struct _catlocs *cl;
			bp->chunkbase[type] = Cmalloc(0,chunksizes[type]-zbp.guarded);
			bp->chunksize[type] = chunksizes[type]-zbp.guarded;
			cl = new_catloc();
			cl->addr = bp->chunkbase[type];
			cl->fptr = bp->catlocs;
			bp->catlocs = cl;
		}
	}
	bp->chunksize[type] -= size;
	p = bp->chunkbase[type];
	bp->chunkbase[type] += size;
	return p;
}
static void *
new_Mnode(struct _bins *bp, int levels, int type)
{
int size;
NodePM p;

	if((p=bp->freenodes[type][levels]))
	{
		bp->freenodes[type][levels] = p->fptr[0];
		p->value = 0;
		return p;
	}
 	size = sizeof(struct _nodeM) + levels * sizeof(void*);
	p = new_chunk(bp, size, type);
	p->levels = levels;
	p->value = 0;
	return p;	
}
static void
free_Mnode(struct _bins *bp, NodePM p, int type)
{
	p->fptr[0] = bp->freenodes[type][p->levels];
	bp->freenodes[type][p->levels] = p;
}
static struct _addr *
new_addr(struct _bins *bp)
{
struct _addr *p;
	if((p=bp->freeaddrlocs))
	{
		bp->freeaddrlocs = p->fptr;
		return p;
	}
	return new_chunk(bp, sizeof(struct _addr), FREEH);
}
static void
free_addr(struct _bins *bp, struct _addr *p)
{
	p->fptr = bp->freeaddrlocs;
	bp->freeaddrlocs = p;
}
static struct _bins *
new_bins(void)
{
struct _bins *p;
	if((p=freebinlocs))
	{
		freebinlocs = p->fptr;
		return p;
	}
 	if(binsize < sizeof(struct _bins))
	{
		binbase = THEWELL(4096);
		binsize = 4096;
	}
	binsize -= sizeof(struct _bins);
	p = (struct _bins*)binbase;
	binbase += sizeof(struct _bins);
	return p;
}
static void
free_bins(struct _bins *p)
{
	p->fptr = freebinlocs;
	freebinlocs = p;
}
static int
getMlevel (struct _bins *p, int binlevel)
{
int level = -1;
int bits = 0;

    while(bits == 0)
    {
		if (p->nbits == 0)
		{
		    p->bits = lrandom();
			p->nbits = 15;
		}
		bits = p->bits & 3;
		p->bits >>= 2;
		p->nbits--;

		if(++level > binlevel)
			break;
    }
    return (level > MAL_MAXLEVEL) ? MAL_MAXLEVEL : level;
}

static void
init_bins(struct _bins *bp, int category)
{
int i;
int binnum = category % 1009;

	bzero(bp, sizeof(struct _bins));
	bp->bincat = category;
	bp->minloc = 0xffffffff;
	bp->fptr = hmap[binnum];
	hmap[binnum] = bp;
	bp->SIZEHheader = new_Mnode(bp, MAL_MAXLEVEL+1, SIZEH);
	bp->FREEHheader = new_Mnode(bp, MAL_MAXLEVEL+1, FREEH);
	bp->USEDHheader = new_Mnode(bp, MAL_MAXLEVEL+1, USEDH);

	for(i = 0; i <= MAL_MAXLEVEL; ++i)
	{
		bp->SIZEHheader->fptr[i] = _NILLL;
		bp->FREEHheader->fptr[i] = _NILLL;
		bp->USEDHheader->fptr[i] = _NILLL;
	}
}

static struct _bins*
getcat(int category)
{
struct _bins *hbp;

	hbp = hmap[category % 1009];
	while(hbp)
	{
		if(hbp->bincat == category)
			return hbp;
		hbp = hbp->fptr;
	}
	return 0;
}
static struct _bins *
initcat(int category)
{
struct _bins *bp;

	if(category == 0)
	{
		bp = &zbp;
		if(zbp.SIZEHheader == 0)
			init_bins(bp, category);
		return bp;
	}
	/* do this to set zbp.guarded properly on startup */
	if(zbp.SIZEHheader == 0)
		initcat(0);

	if((bp = new_bins()))
	{
		init_bins(bp, category);
		return bp;
	}
	return 0;
}
static void *
getspace(struct _bins *bp, unsigned size, unsigned *remainder)
{
unsigned desired;
void *address;
  
	desired = ((size+ALLOCSIZE-1)/ALLOCSIZE)*ALLOCSIZE;
	if(bp->bincat == 0)
	{
		address = THEWELL(desired);
		*remainder = desired - size;
	}
	else
	{
	struct _catlocs *cl;

		if((desired-size) > zbp.guarded)
			desired -= zbp.guarded;
		
		address = Cmalloc(0, desired);
		*remainder = desired - size;

		/* save the gross allocations for the category */
		cl = new_catloc();
		cl->addr = address;
		cl->fptr = bp->catlocs;
		bp->catlocs = cl;
	}
	/* maintain address range info */
	if((unsigned)address < bp->minloc)
		bp->minloc = (unsigned)address;
	if(((unsigned)address + desired) > bp->maxloc)
		bp->maxloc = (unsigned)address + desired;
 	if(bp->minloc < minloc)
 		minloc = bp->minloc;
 	if(bp->maxloc > maxloc)
 		maxloc = bp->maxloc;
	return address;
}
static void
addto_sizelist(struct _bins *bp, AddrP ap)
{
SKIPVARS;

	/* INSERT IN SIZE LIST */
	FINDKEY(SIZEH, ap->size)

	if(node->key == ap->size)
	{/* size node exists */
		ap->fptr = (AddrP)node->value;
		ap->bptr = (AddrP)&node->value;
		if(ap->fptr) ap->fptr->bptr = ap;
		node->value = (unsigned)ap;
	}
	else
	{/* create new size node */
		SETLEVEL(SIZEH)
		node = new_Mnode(bp, level, SIZEH);
		node->key = ap->size;
		node->value = (unsigned)ap;
		ap->fptr = 0;
		ap->bptr = (AddrP)&node->value;
		INSERT()
	}
}
static void
addto_freelist(struct _bins *bp, void *addr, unsigned size)
{
SKIPVARS;
AddrP ap,sp;
unsigned dsize[2];

	/* GET NEW ADDR STRUCT */
	ap = new_addr(bp);
	ap->size = size;

	dsize[1] = dsize[0] = 0; /* sizenode deletion markers */

	/* CHECK FREE LIST */
	FINDKEY(FREEH, (unsigned)addr)

	/* CHECK FOR MERGE OR INSERT */
	if(prev->value && prev->key+((AddrP)prev->value)->size == (unsigned)addr)
	{/* merge with previous block */
		ap->size += ((AddrP)prev->value)->size;

		if(prev->key + ap->size == node->key)
		{/* merge with previous and next block */
			sp = (AddrP) node->value;;
			ap->size += sp->size;

			/* delete size struct for next block */
			UNLINK(sp, 0)

			/* delete next block */
			DELETENODE(FREEH);
		}
		/* delete size struct for prev block */
		sp = (AddrP)prev->value;
		UNLINK(sp, 1)

		/* set new address struct */
		prev->value = (unsigned)ap;
		ap->maddr = prev;
	}
	else if(node->value && (char*)addr + size == (void*)node->key)
	{/* merge with next block */
		sp = (AddrP) node->value;;
		node->key = (unsigned)addr;
		ap->size += sp->size;

		/* unlink size struct for next block */
		UNLINK(sp,0)

		/* set new address struct */
		node->value = (unsigned)ap;
		ap->maddr = node;
	}
	else
	{/* insert in free list */

		SETLEVEL(FREEH)
		node = new_Mnode(bp, level, FREEH);
		node->key = (unsigned)addr;
		node->value = (unsigned)ap;
		ap->maddr = node;
		INSERT()
	}
	addto_sizelist(bp, ap);

	/* Remove sizenodes eliminated by merge */
	if(dsize[0])
	{
		FINDKEY(SIZEH, dsize[0])
		if(node->value == 0)
		  DELETENODE(SIZEH)
	}
	if(dsize[1])
	{
		FINDKEY(SIZEH, dsize[1])
		if(node->value == 0)
		  DELETENODE(SIZEH)
	}
}

LOCAL void* 
Cmemalign(int category, unsigned alignment, unsigned req)
{
SKIPVARS;
NodePM fnode;
unsigned remainder;
unsigned *address;
struct _bins *bp;
unsigned mask, size;


	if(!(bp = getcat(category)))
	  if(!(bp = initcat(category)))
		return 0;
HEAPCHECK
	if(req == 0)
		req = ALIGNMENTM;
	else
		req += ROUNDINGM(req);
	size = req += bp->guarded;

	if(alignment)
	{
		alignment += ROUNDINGM(alignment);
		if(alignment > ALIGNMENTM)
		{
			mask = alignment -1;
			size = req + alignment + bp->guarded;
		}
		else
		{
			alignment = 0;
		}
	}

	/* check sizelist for candidate */
	FINDKEY(SIZEH, size)
	fnode = node;
trynext:
	if(node->key != 0xffffffff)
	{/* found an appropriately sized block */
	AddrP sp = (AddrP)node->value;

		if(!sp && node == fnode)
		{
		NodePM q;
			q = node->fptr[0];
			DELETENODE(SIZEH)
			node = q;
			goto trynext;
		}
		if(!sp)
		{/* no available space at this size */
			node = node->fptr[0];
			goto trynext;
		}

		/* extract some space from this block */
		remainder = node->key - size;
		address = (void*)sp->maddr->key;
		sp->maddr->key += size;
		DETACH(sp);

		if(node->value == 0)
		{/* no more blocks of this size, delete sizenode */
			if(node != fnode)
			  FINDKEY(SIZEH, size)
			DELETENODE(SIZEH)
		}

		if(remainder == 0)
		{/* no remaining space,the node in freelist is exhausted, delete it */
			FINDKEY(FREEH, sp->maddr->key)
			DELETENODE(FREEH)
			free_addr(bp, sp);
		}
		else
		{/* space remains in block, move it to new size loc */
			sp->size = remainder;
			addto_sizelist(bp, sp);
		}
	}
	else
	{
		address = getspace(bp, size, &remainder);
		if(remainder)
		  addto_freelist(bp, ((char*)address)+size, remainder);
	}
	if(alignment)
	{
	unsigned diff;
		if((diff = (unsigned)address & mask))
		{/* move address forward */
		char *naddress;
		unsigned lose;
			lose = alignment - diff;
			naddress = (char*)address + lose;
			addto_freelist(bp, address, lose);
			address = (unsigned*)naddress;
		}
	}
	if(bp->guarded)
	{
	  *address = FRNTGUARD;
	  *((unsigned*)(((char*)address)+req-ALIGNMENTM)) = BACKGUARD;

	}

	FINDKEY(USEDH, (unsigned)address)

	if(node->key == (unsigned)address) {
	  PERROR(pName ":ERROR:allocC:%d: bookkeeping nodes corrupted at:0x%x\n",
	  	category, address);
	}
	SETLEVEL(USEDH)
	node = new_Mnode(bp, level, USEDH);
	node->key = (unsigned)address;
	node->value = req;
	INSERT()	

	return address+bp->addrbump;
}
LOCAL void*
Ccalloc(int category, unsigned cnt, unsigned elem_size)
{
unsigned size = cnt * elem_size;
void* buf;;

  if((buf = Cmalloc(category, size)))
	  bzero(buf, size);
  return buf;
};
LOCAL void
Cfree(int category, void* addr)
{
unsigned cursize;
unsigned *address;
struct _bins *bp;
SKIPVARS;
	if(addr)
	{
		if(!(bp = getcat(category))) {
			PERROR(pName ":ERROR:Cfree:%d: non-existant category at:0x%x\n",category,addr);
		}
HEAPCHECK
		address = (void*) ((unsigned*)addr - bp->addrbump);
		FINDKEY(USEDH, (unsigned)address)
		if(node->key != (unsigned)address) {
		  PERROR(pName ":ERROR:Cfree:%d: bogus address=0x%x\n", category, addr);
		}
		cursize = node->value;
		CHECKGUARDS(Cfree)
		DELETENODE(USEDH)

		addto_freelist(bp, address, cursize);
	}
	else PERROR(pName ":ERROR:Cfree:%d: null pointer\n", category);
}
LOCAL void* 
Crealloc(int category, void* addr, unsigned newsize)
{
SKIPVARS;
unsigned cursize;
unsigned *address;
struct _bins *bp;
NodePM onode;

	if(addr == 0) 
		return Cmalloc(category, newsize);
	else
	{
		if(!(bp = getcat(category))) {
		   PERROR(pName ":ERROR:reallocC:%d: non-existant category at:%x\n",category,addr);
		}
HEAPCHECK 
		if(newsize == 0)
			newsize = ALIGNMENTM;
		else
			newsize += ROUNDINGM(newsize);
		newsize += bp->guarded;

		address = (void*)(((char*)addr)-(bp->guarded/2));
		FINDKEY(USEDH, (unsigned)address)
		if(node->key != (unsigned)address) {
		  PERROR(pName ":ERROR:reallocC:%d: bogus address=0x%x\n", category, addr);
		}
		cursize = node->value;
		node->value = newsize;
		onode = node;

		CHECKGUARDS(reallocC)

		if(newsize == cursize)
			return addr;
		if(newsize > cursize)
		{/* check if block can be extended */
		void *taddr = ((char*)address) + cursize;
		unsigned extendsize = newsize-cursize;

			/* check freelist for an available block at the right address */
			FINDKEY(FREEH, (unsigned)taddr)
			if(node->key == (unsigned)taddr)
			{
			AddrP sp = (AddrP)node->value;
				if(sp->size >= extendsize)
				{/* BLOCK CAN BE EXTENDED INTERNALLY */
					node->key += extendsize;
					sp->size -= extendsize;
					DETACH(sp)
					if(sp->size == 0)
					{/* the extension block is used up, delete this node */
						free_addr(bp, sp);
						DELETENODE(FREEH)
					}
					else
					{/* shift the remainder in the sizelist */
						addto_sizelist(bp, sp);
					}
					/* SUCCESS */
					if(bp->guarded)
					{
						*((unsigned*)(((char*)address)+newsize-ALIGNMENTM))
							= BACKGUARD;
					}
					return addr;
				}
			}
			/* HERE WE COULD CHECK OTHER SOURCES OF SPACE */

			/* can't extend block, malloc some new space */
			if((taddr = Cmalloc(category,newsize-bp->guarded)))
			{
				memmove(taddr,addr,cursize-bp->guarded);
				onode->value = cursize;
				Cfree(category, addr);
			}
			/* SUCCESS */
			return taddr;
		}
		else
		{/* shrink block */
			if(bp->guarded)
			{
				*((unsigned*)(((char*)address)+newsize-ALIGNMENTM))
					= BACKGUARD;
			}
			addto_freelist(bp, ((char*)address)+newsize, cursize-newsize); 
			return addr;
		}
  	}
}
LOCAL void
Cfreecat(int category)
{
struct _bins *bp;

	if(category == 0)
		return;

	if((bp = getcat(category)))
	{
	struct _catlocs *cl = bp->catlocs;
	struct _bins *hbp;
	struct _bins *prev;

		while(cl)
		{/* Space allocated to the category is moved to category 0 */
		void *ql = cl->fptr;

			Cfree(0, cl->addr);
			free_catloc(cl);
			cl = ql;
		}
		/* space for the _bins struct is placed on a free list */
		hbp = hmap[category % 1009];
		prev = 0;
		while(hbp)
		{
			if(hbp->bincat == category)
			{
				if(prev == 0)
					hmap[category % 1009] = hbp->fptr;
				else
					prev->fptr = hbp->fptr;
				free_bins(hbp);
				return;
			}
			prev = hbp;
			hbp = hbp->fptr;
		}
	}
}
LOCAL int
Cmemrange(int category, unsigned *min, unsigned *max)
{
struct _bins *bp;

	if((bp = getcat(category)))
	{
		*min = bp->minloc;
		*max = bp->maxloc;
		return 1;
	}
	return 0;
}
LOCAL int
Cusedrange(int category, unsigned *min, unsigned *max)
{
struct _bins *bp;
NodePM node;
int level;

	if((bp = getcat(category)))
	{
		node = bp->USEDHheader;
		*min = node->fptr[0]->key;
		for(level = bp->USEDHlevel; level >= 0; level--)
		  while(node->fptr[level]->key < 0xffffffff)
			node = node->fptr[level];
		*max = node->key;
		return 1;
	}
	return 0;
}
LOCAL void
Ctotrange(unsigned *min, unsigned *max)
{
	*min = minloc;
	*max = maxloc;
}
LOCAL void
Cguard(int category)
{
struct _bins *bp;

	if(!(bp = getcat(category)))
	  if(!(bp = initcat(category)))
		  return;

	if(!bp->guarded)
	{
		bp->guarded = 2*ALIGNMENTM;
		bp->addrbump = 1;
	}
}
LOCAL void*
Cheapcheck(int category, void *start)
{
struct _bins *bp;
NodePM node,prev;
unsigned *p1,*p2;

	if((bp = getcat(category)))
	{
		if(bp->guarded)
		{
			prev = 0;
			node = bp->USEDHheader;
			while(		(node = node->fptr[0]) != (NodePM)0xffffffff
					&&	node->key != 0xffffffffUL)
			{
				if((void*)node->key > start)
				{
					p1 = (unsigned*)node->key;
					if(*p1 != FRNTGUARD)
					{
						if(prev)
							return (char*)prev->key+ALIGNMENTM;
						else
							return (void*)1;
					}
					p2 = (unsigned*)(((char*)p1)+node->value-ALIGNMENTM);
					if(*p2 != BACKGUARD)
						return (char*)node->key+ALIGNMENTM;
				}
				prev = node;
			}
		}
	}
	return 0;
}
LOCAL void* 
Cmalloc(int category, unsigned size)
{
	return Cmemalign(category, 0, size);
}

LOCAL void* 
Cvalloc(int category, unsigned bytes)
{
  return Cmemalign (category, PAGESIZE, bytes);
}
LOCAL unsigned
Cmallocsize(int category, void* addr)
{
struct _bins *bp;
SKIPVARS;

	if(addr && (bp = getcat(category)))
	{
	unsigned address = (unsigned)((unsigned*)addr - bp->addrbump);
		FINDKEY(USEDH, address)
		if(node->key == address)
			return node->value - bp->guarded;
	}
	return 0;
}

LOCAL int
Cnewcat()
{
static unsigned int cat = BASE_CATEGORY;
	return ++cat;
}
/* ====================== END MULTI-HEAP MALLOC ============================ */


/* ====================== SYMBOL TABLE HANDLERS ============================ */

typedef struct _key
{
	unsigned long k[2];
	unsigned long hv;
} KEY, *KEYP;

typedef struct _nodeS
{/* 40 bytes -- adjust size to suit application */
	unsigned long value[4];	/* 16 bytes */
	unsigned long key[2];	/* 8 bytes */
	struct _nodeS *fptr[4];	/* 16 bytes */
} NodeS, *NodePS;

typedef struct _pbuf
{/* symbol table object */
	int	nbins;			/* number of bins in dictionary */
	int lastbin;		/* for seq access */
	NodePS lastptr;		/* ditto */
	int category;		/* heap number */
	char *chunkbase;	/* node allocation base */
	int chunksize;		/* number of bytes available in current chunk */
	NodePS freelist;	/* list of freed nodes for allocation */
	int level;			/* sorted level */
	int bits;			/* sorted bits */
	int bitcnt;			/* sorted bitcnt */
	NodePS header;		/* sorted header */
	NodePS bins[0];		/* bins if hashed dictionary */
} *PbufP;

#define SYM_MAXLEVEL 12
#define TBL ((PbufP)tbl)

static struct _nodeS _nnil = {{0,0,0,0},{0xffffffff,0xffffffff},{0,0,0,0}};
static struct _nodeS *_NNIL = &_nnil;

static int
getSlevel (PbufP tbl)
{
int level = -1;
int bits = 0;

    while (bits == 0)
    {
		if (tbl->bitcnt == 0)
		{
		    tbl->bits = lrandom();
		    tbl->bitcnt = 15;
		}

		bits = tbl->bits & 3;
		tbl->bits >>= 2;
		tbl->bitcnt--;

		if(++level > tbl->level)
			break;
    }
    return (level > SYM_MAXLEVEL) ? SYM_MAXLEVEL : level;

}

static void
hash(void *key, KEY *cat)
{
	cat->k[0] = ((unsigned long*)key)[0];
	cat->k[1] = ((unsigned long*)key)[1];
	cat->hv = ((cat->k[0] ^ cat->k[1]) * 1103515245UL) + 12345;
}
static void
sym_hash(unsigned long *key, char *symb)
{
int len = strlen(symb);
int i;
	for(i = 0; i < len; ++i)
	  ((unsigned char *)key)[i & 7] ^= symb[i];
	key[0] = ((key[0] ^ key[1]) * 1103515245UL) + 12345;
	key[1] = len;
}
static void *
new_Snode(PbufP tbl, int levels)
{
NodePS p;
int size;
	if(levels <= 3)
	{
		if(tbl->freelist)
		{
			p = tbl->freelist;
			tbl->freelist = p->fptr[0];
			p->fptr[0] = 0;
			return p;
		}
	}
	size = sizeof(struct _nodeS) + ((levels-3) * sizeof(void*));
	if(tbl->chunksize < size)
	{
		tbl->chunkbase = Ccalloc(tbl->category, 1, 4080);
		tbl->chunksize = 4080;
	} 
	tbl->chunksize -= size;
	p = (NodePS)tbl->chunkbase;
	tbl->chunkbase += size;
	return p;
}
static void
free_Snode(PbufP tbl, NodePS node)
{
	bzero(node, sizeof(struct _nodeS));
	node->fptr[0] = tbl->freelist;
	tbl->freelist = node;
}

static void*
NewSymTable(int category, int nbins)
{
PbufP tbl;

	tbl = Ccalloc(category, 1, nbins*sizeof(NodePS) + sizeof(struct _pbuf));
	if(nbins == 0)
	{/* sorted dictionary */
	int i;
		tbl->header = new_Snode(tbl, SYM_MAXLEVEL+1);
		for(i = 0; i <= SYM_MAXLEVEL; ++i)
			tbl->header->fptr[i] = _NNIL;
	}
	tbl->nbins = nbins;
	tbl->category = category;
	return tbl;
}
static int
SymFind(void *tbl, void *key, void *result)
{
NodePS node;

	if(tbl && key)
	{
	  if(TBL->nbins)
	  {/* hashed dictionary */
	  KEY cat;

		hash(key, &cat);
		if((node = TBL->bins[cat.hv % TBL->nbins]))
		{
			do {
				if(		node->key[0] == cat.k[0]
					&&	node->key[1] == cat.k[1])
				{
					if(result)
					  *((NodePS *)result) = node;
					TBL->lastbin = cat.hv % TBL->nbins;
					TBL->lastptr = node;
					return 1;
				}
			 } while((node = node->fptr[0]));
		}
		return 0;
	  }
	  else
	  {/* sorted dictionary */
	  int level;

	    node = TBL->header;
	    for(level = TBL->level; level >= 0; level--)
		{
		  while( KEYLT(node->fptr[level]->key, ((unsigned long*)key)) )
		    node = node->fptr[level];
		}
	    node = node->fptr[0];

		TBL->lastptr = node;
		if(result)
			*((NodePS *)result) = node;
		return (KEYEQ(node->key, ((unsigned long*)key))) ? 1 : 0;
	  }
	}
	return 0;
}
static int
SymFindRange(void *tbl, void *key, void *result)
{/* assumes 4 byte key and value (the value can be bigger) */
NodePS node;

	if(tbl && key)
	{
	  if(TBL->nbins)
	  {/* hashed dictionary */
		return 0;
	  }
	  else
	  {/* sorted dictionary */
	  NodePS prev;
	  int level;

	    node = TBL->header;
	    for(level = TBL->level; level >= 0; level--)
		{
		  while ( node->fptr[level]->key[0] < ((unsigned long*)key)[0] )
		    node = node->fptr[level];
		}
		prev = node;
	    node = node->fptr[0];

		if( node->key[0] == ((unsigned long*)key)[0] )
		{
			TBL->lastptr = node;
			if(result)
				*((NodePS *)result) = node;
			return 1;
		}		
		if( ((unsigned long*)key)[0] < prev->key[0]+prev->value[0] )
		{
			TBL->lastptr = prev;
			if(result)
				*((NodePS *)result) = prev;
			return 1;
		}
		return 0;
	  }
	}
	return 0;
}
static void *
SymInsert(void *tbl, void *key, void *value, int datsiz)
{
NodePS node;

	if(tbl && key)
	{
	  if(TBL->nbins)
	  {/* hashed dictionary */
	  KEY cat;
	  NodePS *binp;
		hash(key, &cat);
		node = new_Snode(tbl, 0);
		TBL->lastbin = cat.hv % TBL->nbins;
		TBL->lastptr = node;
		binp = &TBL->bins[TBL->lastbin];
		if(value && datsiz)
		  memcpy(node, value, MIN(datsiz,16));
		node->key[0] = cat.k[0];
		node->key[1] = cat.k[1];
		node->fptr[0] = *binp;
		*binp = node;
		return node;
	  }
	  else
	  {/* sorted dictionary */
	  int level;
	  NodePS update[SYM_MAXLEVEL+1];

	    node = TBL->header;
	    for (level = TBL->level; level >= 0; level--)
	    {
		  while ( KEYLT(node->fptr[level]->key,((unsigned long*)key)) )
		    node = node->fptr[level];
		  update[level] = node;
		}

	    level = getSlevel(tbl);

	    while(TBL->level < level)
			update[++TBL->level] = TBL->header;

	    node = new_Snode(tbl, level);

		if(value && datsiz)
		  memcpy(node, value, MIN(datsiz,16));
		node->key[0] = ((unsigned long*)key)[0];
		node->key[1] = ((unsigned long*)key)[1];

	    while(level >= 0)
	    {
			node->fptr[level] = update[level]->fptr[level];
			update[level]->fptr[level] = node;
			level--;
    	}
		TBL->lastptr = node;
		return node;
	 }
	}
	return 0;
}
static int
StringFind(void *tbl, char *string, void *result)
{
unsigned long key[2];
struct {
	char *symname;
} *valp;

	key[0] = 0;
	key[1] = 0;
	sym_hash(key, string);

	if(SymFind(tbl, key, &valp))
	{
	unsigned long *key1;
		do {
			if(!strcmp(string, valp->symname))
			{
				if(result)
					*((void**)result) = valp;
				return 1;
			}
			/* Check duplicates */
			if(!SymNext(tbl))
				break;
			SymKey(tbl, &key1);
			SymValue(tbl, &valp);
		} while(KEYEQ(key, key1));
	}
	return 0;
}
static int
StringInsert(void *tbl, char *string, void *result)
{
unsigned long key[2];
struct {
	char *symname;
} *valp;

	key[0] = 0;
	key[1] = 0;
	sym_hash(key, string);
	if(SymFind(tbl, key, &valp))
	{/* hash keys match */
	unsigned long *key1;
		do {
			if(!strcmp(string, valp->symname))
			{
				if(result)
					*((void**)result) = valp;
				return 1;
			}
			/* Check duplicates */
			if(!SymNext(tbl))
				break;
			SymKey(tbl, &key1);
			SymValue(tbl, &valp);
		} while(KEYEQ(key, key1));
	}
	/* NOMATCH */
	valp = SymInsert(tbl, key, &string, 4);
	if(result)
		*((void**)result) = valp;
	return 0;
}
static void
SymDelete(void *tbl, void *key)
{
NodePS node;

	if(tbl && key)
	{
	  if(TBL->nbins)
	  {/* hashed dictionary */
	  KEY cat;
	  NodePS *binp;
	  NodePS prev = 0;

		hash(key, &cat);
		binp = &TBL->bins[cat.hv % TBL->nbins];
		if((node = *binp))
		{
			do {
				if(		node->key[0] == cat.k[0]
					&&	node->key[0] == cat.k[1])
				{
					if(prev)
						prev->fptr[0] = node->fptr[0];
					else
						*binp = node->fptr[0];

					free_Snode(tbl, node);
					if(TBL->lastptr == node)
					{
						TBL->lastptr = 0;
						TBL->lastbin = TBL->nbins;
					}
					return;
				}
				prev = node;
			 } while((node = node->fptr[0]));
		}
	  }
	  else
	  {/* sorted dictionary */
	  int level;
	  NodePS update[SYM_MAXLEVEL+1];

	    node = TBL->header;
	    for(level = TBL->level; level >= 0; level--)
	    {
		  while ( KEYLT(node->fptr[level]->key, ((unsigned long*)key)) )
		    node = node->fptr[level];
		  update[level] = node;
		}
		node = node->fptr[0];

		if( KEYEQ(node->key, ((unsigned long*)key)) )
		{
		    for(level = 0; level <= TBL->level; level++)
		    {
				if (update[level]->fptr[level] == node)
				     update[level]->fptr[level] = node->fptr[level];
				else break;
		    }

		    while((TBL->level > 0) && (TBL->header->fptr[TBL->level] == _NNIL))
			    TBL->level--;

			if(TBL->lastptr == node)
				TBL->lastptr = 0;
			free_Snode(tbl, node);
		}
	  }
	}
}
static int
SymHead(void *tbl)
{/* Set up for sequential access */
int nbins;

	if(tbl)
	{
	  if((nbins = TBL->nbins))
	  {/* hashed dictionary */
	  NodePS node;
	  int i;
		TBL->lastptr = 0;
		for(i = 0; i < nbins; ++i)
		{
			if( (node = TBL->bins[i]) != 0)
			{
				TBL->lastbin = i;
				return 1;
			}
		}
		TBL->lastbin = nbins;
		return 0; /* empty */
	  }
	  else
	  {/* sorted dictionary */
		TBL->lastptr = TBL->header;
		return (TBL->lastptr->fptr[0] == _NNIL) ? 0 : 1;
	  }
	}
	return 0;
}
static int
SymNext(void *tbl)
{/* Move to next sequential entry */
int nbins;

	if(tbl)
	{
	  if((nbins = TBL->nbins))
	  {/* hashed dictionary */
		if(TBL->lastptr && ((TBL->lastptr = TBL->lastptr->fptr[0])))
			return 1;
		else
		{
		int i;
			for(i = TBL->lastbin; i < nbins; ++i)
			{
				if((TBL->lastptr = TBL->bins[i]) != 0)
				{
					TBL->lastbin = i+1;
					return 1;
				}
			}
			return 0;
		}
	  }
	  else
	  {/* sorted dictionary */
		if(TBL->lastptr)
		{
			if(TBL->lastptr != _NNIL)
				TBL->lastptr = TBL->lastptr->fptr[0];
			return (TBL->lastptr == _NNIL) ? 0 : 1;
		}
	  }
	}
	return 0;
}
static void
SymGetMark(void *tbl, void *markptr)
{
	if(tbl && markptr)
	{
		((long*)markptr)[0] = TBL->lastbin;
		((long*)markptr)[1] = (long)TBL->lastptr;
	}
}
static int
SymMarkNext(void *tbl, void *mark)
{/* Mark current position, and move to next sequential entry */
	SymGetMark(tbl, mark);
	return SymNext(tbl);
}
static void
SymSetMark(void *tbl, void *markptr)
{
	if(tbl && markptr)
	{
		TBL->lastbin = ((long*)markptr)[0];
		TBL->lastptr = (NodePS)((long*)markptr)[1];
	}
}
static void
SymKey(void *tbl, void *keyptr)
{/* Retrieve key info pointer for current spot */

	if(tbl && keyptr && TBL->lastptr)
		*((unsigned long**)keyptr) = &TBL->lastptr->key[0];
}
static void
SymValue(void *tbl, void *datptr)
{/* Retrieve value pointer for current spot */

	if(tbl && datptr && TBL->lastptr)
		*((unsigned long**)datptr) = &TBL->lastptr->value[0];
}

/* ==================== END SYMBOL TABLE HANDLERS ========================== */

/* ========================== OPTIMIZATION ================================= */
static int
forward(unsigned char *p)
{
unsigned char *next;

	do {
		next = (void*)((Pop)p)->next;
		while(		*next == 0
				||	*next == lineop
				||	*next == labelop)
			next = (void*)((Pop)next)->next;

		if(*next == endop)
		{
			if(*p == *(next+8))
			{
				*p = 0;
				*next = 0;
				return 1;
			}
			return 0;
		}
	} while(forward(next));

	return 0;
}
static void
eliminate_extraneous_infops(Piv iv, int level)
{
Pafile pf;
unsigned char *p;
int i;
	for(i = 0; i < iv->numfiles; ++i)
	{
		iv->filenum = i;
		pf = iv->files[i];
		if(!(p = pf->prog_p))
			continue;
		if(pf->header_p->hdr.opt_level >= level)
			continue;
		pf->header_p->hdr.opt_level = level;
		while(*p != endfileop)
		{
			switch(*p)
			{
				case	unopop:
				case	arrayelemop:
				case	ptrelemop:
				case	strelemop:
				case	ptrdimsop:
				case	arraydimsop:
					forward(p);
					break;
			}
			p = POP->next;
		}
	}
}
static void
clean_temps(Piv iv)
{
long *key;
long *val;
long hitemp = iv->first_temp & 0xffff0000;

	if(iv->temps_written == 0)
		return;

	if(SymHead(iv->tmptbl))
	{
		while(SymNext(iv->tmptbl))
		{
			SymKey(iv->tmptbl, &key);

			if((key[0] & 0xffff0000) == hitemp)
			{
			char *ptr;
			long saveit;

				SymValue(iv->tmptbl, &val);
				saveit = val[1];
				ptr = (void*)val[0];
				val[0] = 0;	/* allow reuse of this slot */
				val[1] = 0;

				while(ptr)
				{
				void *nptr = (void*)((PopT)ptr)->tmpnum;
					((PopT)ptr)->tmpnum = key[0];

					if(!saveit)
					{
					unsigned char *p = ptr-8;					
					unsigned char op = *p;

						*p = 0;						
						++iv->killop;
						if(op == duptmpop)
						{/* special test for post increment */
							p = POP->next;
							p = POP->next;
							if(*p == grabop)
								*p = 0;
						}
					}
					ptr = nptr;
				}
			}
		}
		if(!hitemp)
			iv->temps_written = 0;
	}
}

static void
read_temp(Piv iv, PopT ptr, unsigned long last)
{
unsigned long key[2];
long *result;

	if(last == ptr->tmpnum)
		return;

	key[0] = ptr->tmpnum;
	key[1] = 0;

	if(SymFind(iv->tmptbl, key, &result))
	{
		result[1] = 1;
	}
	else PERROR(pName ":SYSERROR: read temp %d not found\n", key[0]);
}
static int 
reading_self(unsigned char *p, long tmpnum)
{
	if((p[2]&0xe0) == OPTEMP || (p[2]&0xe0) == OPRET)
	{
		  return 1;
	}
	if((p[3]&0xe0) == OPTEMP || (p[3]&0xe0) == OPRET)
	{
		return 1;
	}
	return 0;
}
static long
write_temp(Piv iv, PopT ptr, unsigned char opcode)
{
long key[2];
long val[2];
long *result;
long hitemp = ptr->tmpnum & 0xffff0000;

	if(ptr->atype & A_MEMADDR && opcode < duptmpop)
	{/* actually reading from this destination slot */
		read_temp(iv, ptr, 0);
		return 0;
	}

	if(hitemp > (iv->first_temp & 0xffff0000))
	{/* Inner block, CompoundExp or NestedFunc */
		iv->first_temp = hitemp + 1;
	}
	else if(hitemp < (iv->first_temp & 0xffff0000))
	{/* Exit inner block */
		if(!reading_self(((char*)ptr)-8, ptr->tmpnum))
		{
			while(hitemp < (iv->first_temp & 0xffff0000))
			{
				clean_temps(iv);
				iv->first_temp -= 0x00010000;
			}
		}
	}
	if(ptr->tmpnum == iv->first_temp)
	{
		if(!reading_self(((char*)ptr)-8, ptr->tmpnum))
			clean_temps(iv);
	}
	++iv->temps_written;
	key[0] = ptr->tmpnum;
	key[1] = 0;

	if(SymFind(iv->tmptbl, key, &result))
	{
	PopT optr = (PopT)result[0];
		result[0] = (long)ptr;
		ptr->tmpnum = (long)optr;
	}
	else
	{
		val[0] = (long)ptr;
		val[1] = 0;

		SymInsert(iv->tmptbl, key, val, 8);
		ptr->tmpnum = 0;
	}
	return key[0];
}
static void
eliminate_unused_temps(Piv iv, int level)
{
Pafile pf;
unsigned char *p;
int i;
long last_write;

	iv->tmptbl = NewSymTable(iv->category, 111);
	for(i = 0; i < iv->numfiles; ++i)
	{
		iv->filenum = i;
		pf = iv->files[i];
		if(pf->header_p->hdr.opt_level >= level)
			continue;
		pf->header_p->hdr.opt_level = level;
rekill:
		if(!(p = pf->prog_p))
			continue;
		iv->first_temp = 1;
		iv->temps_written = 0;
		iv->killop = 0;
		while(*p != endfileop)
		{
			while(*p < labelop)
			{
				if(*p == truthop)
				{/* truthops of single chars are unnecessary */
				  if((p[2]&0xe0) == OPTEMP)
				  {
					if(((PopT)(p+20))->dsize == 1)
					{
						if(((PopT)(p+8))->tmpnum == ((PopT)(p+20))->tmpnum)
						{
							if(!(((PopT)(p+20))->atype & A_MEMADDR))
								*p = 0;
							break;
						}
						else
						{/* may be needed for code generation */
							*p = aliastmpop;
						}
					}
				  }
				}
				if(*p)
				{
					if(		*p == jmptrueop 
						||  *p == jmpfalseop
						||	*p == ljmptrueop
						||	*p == ljmpfalseop)
						read_temp(iv,(PopT)(p+4), 0);
					if(*p == retdataop)
					{
						read_temp(iv, (PopT)p, 0);
					}
					else
					{
						last_write = 0;
						if((p[1]&0xe0) == OPTEMP)
							last_write = write_temp(iv, (PopT)(p+8), *p);
						if((p[2]&0xe0) == OPTEMP || (p[2]&0xe0) == OPRET)
							read_temp(iv, (PopT)((p+8+(p[1]&0x1f))), last_write);
						if((p[3]&0xe0) == OPTEMP || (p[3]&0xe0) == OPRET)
							read_temp(iv, (PopT)((p+8+(p[1]&0x1f))+(p[2]&0x1f)), last_write);
					}
				}
				break;
			}
			p = POP->next;
		}
		do {
			clean_temps(iv);
			iv->first_temp -= 0x00010000;
		} while(iv->first_temp > 0);
		if(iv->killop)
		{
			goto rekill;
		}
	}
}
static void
retarget_jmps(Piv iv, int level)
{
Pafile pf;
unsigned char *p;
int i;
	for(i = 0; i < iv->numfiles; ++i)
	{
		iv->filenum = i;
		pf = iv->files[i];
		if(!(p = pf->prog_p))
			continue;
		if(pf->header_p->hdr.opt_level >= level)
			continue;
	}
}


static void
optimize(Piv iv)
{
	eliminate_extraneous_infops(iv, 50);
	eliminate_unused_temps(iv, 51);
	retarget_jmps(iv, 52);
}
/* ========================== END OPTIMIZATION ============================= */
/* ====================== BASIC INPUT FILE PROCESSING ====================== */
static long
label_insert(Piv iv, long label, int filenum, unsigned char *p)
{
long *result;
struct {
	long k1;
	long k2;
} key;

struct {
	long newlabel;
} val;

	key.k1 = label;
	key.k2 = filenum;

	/* check for duplicate label -- they happen */
	if(SymFind(iv->labeltbl, &key, &result))
	{
		if(*p == labelop)
			*p = 0; /* kill the instruction */
		return 0;
	}

	val.newlabel = ++iv->lastlabel;
	SymInsert(iv->labeltbl, &key, &val, 4);

#if REALLY_NEED_OFFSETS
	key.k1 = val.newlabel;
	val.newlabel = -1;
	SymInsert(iv->newlabeltbl, &key, &val, 4);
#endif

	return iv->lastlabel;
}
static long
label_find(Piv iv, long label, int filenum)
{
struct {
	long k1;
	long k2;
} key;

long *result;

	key.k1 = label;
	key.k2 = filenum;

	if(SymFind(iv->labeltbl, &key, &result))
		return *result;
	else
		return 0;
}
#if REALLY_NEED_OFFSETS
static void
newlabel_insert(Piv iv, long label)
{
struct {
	long k1;
	long k2;
} key;

long *result;

	key.k1 = label;
	key.k2 = iv->filenum;
	if(SymFind(iv->newlabeltbl, &key, &result))
	{
		*result = iv->out_offset + iv->func_offset;
	}
	else PERROR(pName ":SYSERROR: Label %d not found\n", label);
}
static long
newlabel_fix(Piv iv, long label)
{
	if(label)
	{
	struct {
		long k1;
		long k2;
	} key ;

	long *val;

		key.k1 = label;
		key.k2 = iv->filenum;
		if(SymFind(iv->newlabeltbl, &key, &val))
		{
			return val[0];
		}
	}
	return label;
}
#endif /* REALLY_NEED_OFFSETS */

static void
extern_insert(Piv iv, unsigned char *p, int filenum)
{
struct {
	short k1;
	short k2;
	long k3;
} key;
struct {
	unsigned char *p;
} val;

	key.k1 = GS(POPI->s.symnum);
	key.k2 = filenum;
	key.k3 = 0;

	val.p = p;
	SymInsert(iv->extrntbl, &key, &val, 4);
}
static void
reloc_insert(Piv iv, int fileno, unsigned char *p)
{
struct _rkey key;
struct _rval val;

	key.spot = GL(POPI->reloc.spot);	/* reloc target offset */
	key.fileno = (short)fileno;			/* fileno */
	key.opcode = *p;					/* opcode */
	key.rsize = GL(POPI->reloc.rsize);	/* reloc size */

	val.p = p;								/* pointer to input buffer */
	val.base = (void*)GL(POPI->reloc.base);	/* base of data object pointed to */
	val.offset = GL(POPI->reloc.offset);	/* offset to be added to base */
	val.rsym = GS(POPI->reloc.rsym);		/* symbol number if external */
	SymInsert(iv->reloctbl, &key, &val, 14);
}
static void
data_insert(void *tbl, unsigned long offset,
			unsigned long size, void *p, void *prevp)
{
static long locid = 1;
struct {
	unsigned long k1;
	long k2;
} key;
struct {
	unsigned long size;
	void *p;
	void *prevp;
	long locid;
} val;
unsigned char opcode, prevopcode = 0;

	key.k1 = offset;
	key.k2 = 0;

	val.size = size;
	val.p = p;
	val.prevp = prevp;
	val.locid = 0;
	opcode = *((unsigned char *)p);
	if(prevp)
	{
		prevopcode = *((unsigned char*)prevp);
		if(		prevopcode != glodatop
			&&	prevopcode != glofuncop
			&&	prevopcode != extfuncop
			&&	prevopcode != globssop)
		{
			val.locid = locid++;
		}	
	}
	SymInsert(tbl, &key, &val, sizeof(val));
}

static void
global_insert(Piv iv, Pafile pf, unsigned char *p)
{
unsigned long key[2];
struct _gloval val;
PopI pp;
unsigned char opcode = *p;

	if(opcode == extvarop)
		pp = POPI;
	else
		pp = (PopI)(POP->next+8);

	key[0] = 0;
	key[1] = 0;

	val.symnum = GS(pp->s.symnum);
	val.symname = pf->symaddr[val.symnum];
	val.p = p;
	val.pf = pf;
	if(val.symnum < 0 || val.symnum >= pf->numsyms)
	{
		PERROR(pName ":SYSERROR: BAD SYMNUM=%d opcode=%d\n", val.symnum, opcode);
	}
	sym_hash(key, val.symname);

	/* Duplicate entries are allowed */
	SymInsert(iv->gbltbl, key, &val, sizeof(val));
}

static int
setup_nodelinks(Piv iv, char *infile_name, void *inbuf, int insize)
{
unsigned char *p = inbuf;
unsigned char *endbuf = inbuf+insize;
Pafile pf=0;
int lastline = 0;
unsigned char *funcp;
unsigned char *nfuncp;

	while(p < endbuf && *p != endallop)
	{
	unsigned char *q = p;
		if(iv->debug >= '5')
		{
		 cfeprintf("OP(%u '%s' p=%p line=%d)\n", *p, oxgenops[*p], p, lastline);
		}
		switch(*p)
		{
			case headerop:
				if(iv->numfiles >= 1024) {
					PERROR(pName ": Sorry, too many files\n");
				}
				pf = iv->files[iv->numfiles] = 
					Ccalloc(iv->category, 1, sizeof(struct _afile));
				pf->filenum = iv->numfiles++;
				pf->file_p = p;
				pf->header_p = POPI;
				if(iv->strip)
				{/* Gonna strip declarations and line numbers */
					pf->header_p->hdr.target_debugger = 0;
				}
				break;

			case dataop:
				pf->size_p = POPI;
				pf->thunk_offset = GL(POPI->dat.thunk_offset);
				pf->bss_offset = GL(POPI->dat.bss_offset);
				break;
			case gfuncdefop:
			case sfuncdefop:
				if(pf->prog_p == 0)
					pf->prog_p = p;
				funcp = p;
				break;
			case funcexitop:
				PS(((PopI)(funcp+8))->funcdef.tempmax) = GL(POPI->funcexit.tempmax);
				break;
			case nestedfuncdefop:
				nfuncp = p;
				break;
			case nestedfuncexitop:
				PS(((PopI)(nfuncp+8))->funcdef.tempmax) = GL(POPI->funcexit.tempmax);
				break;
			case segdefop:
				if(pf->seg_p == 0)
					pf->seg_p = p;
				pf->numsegs += 1;
				iv->numsegs += 1;
				break;
			case lineop:
				lastline = GL( POPI->line.line );
				if(iv->strip)
					*p = 0;		/* strip line numbers */
				break;
			case declop:
				if(iv->strip)
				{/* strip declarations */
					do {
						*p = 0;
						q += (long)GL(POP->next);
						POP->next = q;
						p = q;
					} while(*p != endop);					
					*p = 0;
				}
				else
				{
					if(pf->decl_p == 0)
						pf->decl_p = p;
					pf->numdecls += 1;
					iv->numdecls += 1;
				}
				break;
			case switchidop:
				if(pf->switch_p == 0)
					pf->switch_p = p;
				break;
			case labelop:
				PL( POP->data ) = 
						label_insert(iv, GL( POP->data ), pf->filenum, p);
				break;			
			case symbop:
				pf->numsyms = GL(POP->data);
				iv->numsyms += pf->numsyms;
				break;
			case symblockop:
				pf->symtext_p = p + 12;
				goto blka;
			case stringblockop:
			case datablockop:
			case mallocblockop:
			case thunkblockop:
			{
			long size;
				if(pf->data_p == 0)
					pf->data_p = p;
blka:
				size = GL(POP->data);
				q += size+((4-(size&3))&3);
				break;
			}
			case glofuncop:
			case extfuncop:
			case glodatop:
			case globssop:
			case extvarop:
			case bssblockop:
				if(pf->data_p == 0)
					pf->data_p = p;
				break;
			case maxtempop:
				pf->maxtemp = GL(POP->data);
				pf->maxtempclass = GL(POP->data1);
				pf->maxtemp_p = p;
				break;
		}
		q += (long)GL(POP->next);
		POP->next = q;
		p = q;
	}
	if(*p != endallop)
	{
		PERROR(pName ":ERROR: Malformed input file: %s\n", infile_name);
	}
	return 0;
}
static void
setup_syms_decls(Piv iv)
{
int i;

	for(i = 0; i < iv->numfiles; ++i)
	{
	int symnum = 0;
	Pafile pf = iv->files[i];
	unsigned char *p = pf->file_p;
	unsigned char *prevp = 0;

		pf->symaddr = Ccalloc(iv->category, sizeof(void*), pf->numsyms+1);
		pf->decladdr = Ccalloc(iv->category, sizeof(void*), pf->numdecls+1);

		while(*p != endfileop)
		{
			switch(*p)
			{
				case	symoffsetop:
				  pf->symaddr[symnum] = pf->symtext_p + GL(POP->data);
				  ++symnum;
				  break;

				case	declop:
				  pf->decladdr[GS(POPI->dcl.declnum)] = p;
				  break;

				case	relocop:
				case	extlocop:
				  ++pf->numrelocs;
				  reloc_insert(iv, i, p);
				  break;

				case	glodatop:
				case	globssop:
				case	glofuncop:
				case	extfuncop:
				  global_insert(iv, pf, p);
				  break;

				case	extvarop:
				  extern_insert(iv, p, i);
				  global_insert(iv, pf, p);
				  break;

				case	stringblockop:
				case	datablockop:
				case	mallocblockop:
				case	thunkblockop:
				case	bssblockop:
				  if(!pf->datatbl)
					pf->datatbl = NewSymTable(iv->category, 0);  /* sorted */

				  data_insert(pf->datatbl,GL(DATI.offset),GL(DATI.size),
						  	p, prevp);
				  if(*p == thunkblockop) {
					PL(POP->data5) = label_find(iv, GL(POP->data5), i);
				  }
				  break;

				case	jmploopop:
				case	jmpcontinueop:
				case	jmpbreakop:
				case	jmpgotoop:
				case	jmptrueop:
				case	jmpfalseop:
				case	ljmptrueop:
				case	ljmpfalseop:
				case	funcstartop:
				case	funcstopop:
				case	casevalop:
				case	switchop:
				  PL(POP->data) = label_find(iv, GL(POP->data), i);
				  break;
			}
			prevp = p;
			p = POP->next;
		}
	}
}
static int
sym_insert(Piv iv, char *symname, int symnum)
{/* Used only for combining symbols in link phase */
struct {
	char *symname;
	int symnum;
} *valp;

	if(StringInsert(iv->symtbl, symname, &valp))
		return -valp->symnum;	/* MATCH */
	valp->symnum = symnum;
	return symnum;
}
static void
combine_syms_decls(Piv iv)
{
int i,j;
Pafile pf;
int numsyms;
int numdecls;

	/* COMBINE SYMBOLS */
	pf = iv->files[0];
	numsyms = pf->numsyms;
	pf->symtran = Ccalloc(iv->category, sizeof(short), pf->numsyms+1);
	memcpy(iv->symaddr, pf->symaddr, sizeof(void*) * numsyms);


	for(i = 0; i < numsyms; ++i)
	{/* file 0 */
		sym_insert(iv, pf->symaddr[i], i);
		pf->symtran[i] = i;
	}
	for(i = 1; i < iv->numfiles; ++i)
	{
	int start;

		pf = iv->files[i];
		pf->symtran = Ccalloc(iv->category, sizeof(short), pf->numsyms+1);
		if(pf->header_p->hdr.target_debugger)
			start = 1;
		else
			start = 3;
		for(j = start; j < pf->numsyms; ++j)
		{
		int k;
		  if((k = sym_insert(iv, pf->symaddr[j], numsyms)) > 0)
		  { /* new entry */
			iv->symaddr[numsyms++] = pf->symaddr[j];
			pf->symtran[j] = k;
		  }
		  else pf->symtran[j] = -k;
		}
	}
	iv->numsyms = numsyms;

	/* COMBINE DECLARATIONS */
	pf = iv->files[0];
	numdecls = pf->numdecls;
	pf->decltran = Ccalloc(iv->category, sizeof(short), numdecls+1);
	memcpy(iv->decladdr, pf->decladdr, sizeof(void*) * numdecls);
	for(i = 0; i < numdecls; ++i)
	{/* file 0 */
		pf->decltran[i] = i;
	}
	for(i = 1; i < iv->numfiles; ++i)
	{
		pf = iv->files[i];
		pf->decltran = Ccalloc(iv->category, sizeof(short), pf->numdecls+1);
		if(pf->numdecls < 21)
			continue;
		for(j = 1; j <= 21; ++j)
			pf->decltran[j] = j;
		for(j = 22; j < pf->numdecls; ++j) {
			iv->decladdr[numdecls] = pf->decladdr[j];
			pf->decltran[j] = numdecls++;
		}
	}
	iv->numdecls = numdecls;
}

static void
link_dups(Piv iv, int dupcnt, struct _gloval *valp[])
{
int i;
int vars[5] = {0,0,0,0,0};
unsigned long cdsize = 0;
unsigned long cdoffset = 0;
short cdfile = 0;
int cdnum = 0;
short segid = 0;

#define GDAT vars[0]
#define GBSS vars[1]
#define GFUNC vars[2]
#define EVAR vars[3]
#define EFUNC vars[4]

	/* Count the types of matches */
	for(i = 0; i <= dupcnt; ++i)
		vars[*(valp[i]->p) - glodatop] += 1;

	/* Check for errors */
	if(		GDAT > 1 
		||	GFUNC > 1
		||	(GFUNC && (GDAT || GBSS || EVAR))
		||	(EFUNC && (GDAT || GBSS || EVAR)))
	{
		++iv->errors;
		for(i = 0; i < dupcnt; ++i)
		{
			PWARN(pName ": Symbol `%s' multiply defined or mistyped.\n",
			  	valp[i]->symname);
			PWARN(pName":  In file: `%s'\n", valp[i]->pf->symaddr[INFILE_SYMNUM]);
		}
		return;
	}
	if(EFUNC && GFUNC)
	{/* match up functions */
	Pop dp;
		for(i = 0; i <= dupcnt; ++i)
		  if(*(valp[i]->p) == glofuncop)
			break;
		dp = (Pop)((Pop)valp[i]->p)->next;	/* points to thunkblockop */

		cdoffset = GL(dp->data1);			/* save this offset */
		cdfile = valp[i]->pf->filenum;		/* save this file */
		for(i = 0; i <= dupcnt; ++i)
		{
		  if(*(valp[i]->p) == extfuncop)
		  {
			*(valp[i]->p) = 0;					/* convert to nilop */
			dp = (Pop)((Pop)valp[i]->p)->next;	/* points to thunkblockop */

			/* Kill the thunkblock */
			*((char*)dp) = 0;
			PL(dp->data4) = cdoffset;			/* use this offset for access */
			PS(((short*)dp)[1]) = cdfile;		/* fileno to unused slots */
		  }
		}
	}
	else if(EFUNC)
	{/* multiple references to external function */
	Pop	dp = (Pop)((Pop)valp[0]->p)->next;	/* points to first thunkblockop */

		cdoffset = GL(dp->data1);			/* save first offset */
		cdfile = valp[0]->pf->filenum;		/* save first file */
		for(i = 1; i <= dupcnt; ++i)
		{/* Kill all thunkblocks except the first */
			*(valp[i]->p) = 0;					/* convert to nilop */
			dp = (Pop)((Pop)valp[i]->p)->next;	/* points to thunkblockop */
			*((char*)dp) = 0;
			PL(dp->data4) = cdoffset;			/* use this offset for access */
			PS(((short*)dp)[1]) = cdfile;		/* fileno to unused slots */
		}
	}
	else if(GBSS)
	{/* comdefs */
	int multsize = 0;

		/* PICK THE BIGGEST GLOBAL BSS (comdef) */
		for(i = 0; i <= dupcnt; ++i)
		{
		Pop dp = (Pop)((Pop)valp[i]->p)->next;	/* points to bssblockop */
		  if((short)dp->data4 && segid == 0)
		  {
			segid = (short)dp->data4;
		  }
		  else if((short)dp->data4 && (short)dp->data4 != segid)
		  {
			++iv->errors;
			PWARN(pName ": Variable `%s' defined in multiple segments.\n",
				valp[i]->symname);
			PWARN(pName ":  In file: `%s'\n", valp[i]->pf->symaddr[INFILE_SYMNUM]);
		  }
		  if(*(valp[i]->p) == globssop)
		  {
		  long size = GL(dp->data);
			if(cdsize && size != cdsize)
				multsize = 1;
			if(size > cdsize) {
				cdsize = size;
				cdoffset = GL(dp->data1);
				cdfile = valp[i]->pf->filenum;
				cdnum = i;
			}
		  }
		}
		if(GDAT)
		{
		  /* INITIALIZED DATA WILL ALWAYS OVERRIDE BSS */
		  for(i = 0; i <= dupcnt; ++i)
		  {
			if(*(valp[i]->p) == glodatop)
			{
			Pop dp = (Pop)((Pop)valp[i]->p)->next;	/* points to datablockop */
			long size = GL(dp->data);
			  if(cdsize && size != cdsize)
			  	multsize = 1;
			  if(size < cdsize)
			  {
			++iv->errors;
			PWARN(pName ": Initialized variable `%s' of size (%d)\n",
			  valp[i]->symname, size);
			PWARN(pName ":  In file: `%s'\n",
			  valp[i]->pf->symaddr[INFILE_SYMNUM]);
			PWARN(pName ":  Is incommensurate with common size (%d).\n",
			  cdsize);
			  }
			  else
			  {
				  cdsize = size;
				  cdoffset = GL(dp->data1);
				  cdfile = valp[i]->pf->filenum;
				  cdnum = i;
			  }
			}
		  }
		}
		if(multsize)
		{
		  PWARN(pName ":warning: Common Variable `%s' has multiple sizes.\n",
			  valp[0]->symname);
		  for(i = 0; i <= dupcnt; ++i)
		  {
		  unsigned char opcode = *(valp[i]->p);
		    if(opcode == globssop || opcode == glodatop)
		    {
				PWARN(pName ":warning:Size=%d in file: `%s'\n",
				  GL(((Pop)((Pop)valp[i]->p)->next)->data),
				  valp[i]->pf->symaddr[INFILE_SYMNUM]);
			}
		  }
		}
		/* FINALLY, LINK COMMONS TO THE CHOSEN ONE */
		for(i = 0; i <= dupcnt; ++i)
		{
		  if(i != cdnum && *(valp[i]->p) == globssop)
		  {
		  Pop dp = (Pop)((Pop)valp[i]->p)->next;	/* points to bssblockop */

			*(valp[i]->p) = 0;			/* globssop becomes nilop */
			*((char*)dp) = 0;			/* bssblockop becomes nilop */
			PL(dp->data4) = cdoffset;	/* use this new offset for access */
			PS(((short*)dp)[1]) = cdfile;	/* put fileno in unused slots */
		  }
		}
	}
	else if(GDAT)
	{
		for(i = 0; i <= dupcnt; ++i)
		{
		Pop dp = (Pop)((Pop)valp[i]->p)->next;	/* points to datablockop */
		  if((short)dp->data4 && segid == 0)
		  {
			segid = (short)dp->data4;
		  }
		  else if((short)dp->data4 && (short)dp->data4 != segid)
		  {
			++iv->errors;
			PWARN(pName ": Variable `%s' defined in multiple segments.\n",
				valp[i]->symname);
			PWARN(pName ": In file: `%s'\n", valp[i]->pf->symaddr[INFILE_SYMNUM]);
		  }
		  if(*(valp[i]->p) == glodatop)
		  {
			cdsize = GL(dp->data);
			cdoffset = GL(dp->data1);
			cdfile = valp[i]->pf->filenum;
			cdnum = i;
			break;
		  }
		}
	}
	if(EVAR && (GDAT || GBSS))
	{/* match up variables */
		/* LINK EXTERNS TO THE CHOSEN ONE */
		for(i = 0; i <= dupcnt; ++i)
		{
		  if(*(valp[i]->p) == extvarop)
		  {
		  Pop dp = (Pop)valp[i]->p;

			*((char*)dp) = 0;			/* extvarop becomes nilop */
			PL(dp->data1) = cdoffset;	/* use this new offset for access */
			PS(((short*)dp)[1]) = cdfile;	/* put fileno in unused slots */
			PS(dp->data4) = segid;
			break;
		  }
		}
	}
#undef GDAT
#undef GBSS
#undef GFUNC
#undef EVAR
#undef EFUNC

}
static void
link_globals(Piv iv)
{
	if(SymHead(iv->gbltbl))
	{
	struct _gloval *valp[1024];	/* pointers to symtable value structs */

	  /* Pass over the sorted symbol table and process duplicate entries */
	  while(SymNext(iv->gbltbl))
	  {
	  unsigned long *key;
	  long mark[2];							/* Table position saver */
	  int dupcnt = 0;
		SymKey(iv->gbltbl, &key);			/* Pointer to first key */
		SymValue(iv->gbltbl, &valp[0]);		/* Pointer to first value */

		while(SymMarkNext(iv->gbltbl, mark))
		{/* Look forward for duplicates */
		unsigned long *key1;
		  SymKey(iv->gbltbl, &key1);				/* Pointer to next key */
		  if(KEYEQ(key, key1))
		  {/* Hashed keys match, check the strings */
			SymValue(iv->gbltbl, &valp[dupcnt+1]);	/* Pointer to next value */
			if(!strcmp(valp[dupcnt]->symname, valp[dupcnt+1]->symname))
			{/* Duplicate entry found */
				++dupcnt;
				continue;
			}
		  }
		  break;
		}
		if(dupcnt > 0)
		{/* Process a collection of duplicate symbol names */
		  link_dups(iv, dupcnt, valp);
		}
		SymSetMark(iv->gbltbl, mark);

	  }/* END: while(SymNext) */
	}/* END: if(SymHead) */
}
static void
realloc_data(Piv iv)
{
int i;
Pafile pf;
unsigned char *p, *prevp;
unsigned long offset = 0;

	iv->datatbl = NewSymTable(iv->category, 0); 	/* sorted table */

	for(i = 0; i < iv->numfiles; ++i)
	{
		pf = iv->files[i];
		p = pf->data_p;
		prevp = 0;
		while(*p != endfileop)
		{
			if(		*p == datablockop
				||	*p == mallocblockop
				||	*p == stringblockop)
			{
				PL(POP->data1) = offset;				
				data_insert(iv->datatbl, offset, GL(POP->data), p, prevp);
				offset += GL(POP->data);
				ROUNDUP(offset, 4);
			}
			prevp = p;
			p = POP->next;
		}
	}
	iv->thunk_offset = offset;

	for(i = 0; i < iv->numfiles; ++i)
	{
		pf = iv->files[i];
		p = pf->data_p;
		prevp = 0;
		while(*p != endfileop)
		{
			if(*p == thunkblockop)
			{
				PL(POP->data1) = offset;
				data_insert(iv->datatbl, offset, GL(POP->data), p, prevp);
				offset += GL(POP->data);
				ROUNDUP(offset, 4);
			}
			prevp = p;
			p = POP->next;
		}
	}
	iv->bss_offset = offset;

	for(i = 0; i < iv->numfiles; ++i)
	{
		pf = iv->files[i];
		p = pf->data_p;
		prevp = 0;
		while(*p != endfileop)
		{
			if(*p == bssblockop)
			{
				PL(POP->data1) = offset;
				data_insert(iv->datatbl, offset, GL(POP->data), p, prevp);
				offset += GL(POP->data);
				ROUNDUP(offset, 4);
			}
			prevp = p;
			p = POP->next;
		}
	}
	iv->total_size = offset;
}
static void
reset_data_relocs(Piv iv)
{/* Pass over initialized data and set new offsets in each relocatable slot */
struct _data {/* datatbl node */
/* value area 16 bytes */
	unsigned long size;
	unsigned char *p;
	unsigned char opcode;
	unsigned char unused[7];
/* key area 8 bytes */
	unsigned long offset;
	long unused1;
};
	/* PASS OVER ALL THE ENTRIES IN `reloctbl' */
	if(SymHead(iv->reloctbl))
	{
		while(SymNext(iv->reloctbl))
		{
		struct _rkey *kp;
		struct _rval *vp;
		struct _data *dp, *ndp;
		unsigned char *p;
		Pafile pf, npf;
		unsigned long object_base;
		int noset = 0;

		  SymKey(iv->reloctbl, &kp);
		  SymValue(iv->reloctbl, &vp);
		  npf = pf = iv->files[kp->fileno];	/* pointer to file struct */
		  p = vp->p;		/* pointer to relocop in input buffer */

		  if(kp->opcode == extlocop)
		  {/* External variable */
		  short key[4];
		  struct {
		  unsigned char *p;	/* pointer to extvarop in input buffer */
		  } *ep;
			key[0] = vp->rsym; 		/* external symbol number */
			key[1] = pf->filenum;
			key[2] = 0;
			key[3] = 0;

			/* LOOK UP THE EXTERNAL SYMBOL */
			if(SymFind(iv->extrntbl, key, &ep) && *(ep->p) == 0)
			{/* symbol exists and the extvarop was filled in */

				npf = iv->files[GS( ((short*)(ep->p))[1] )];
				PL( POPI->reloc.base ) = GL( ((Pop)(ep->p))->data1 );
				*p = relocop;	/* switch input file from `extlocop' */
			}
			else
			{/* Not found or not filled in, leave it alone */
				noset = 1;
			}
		  }

		  /* RESET THE ENTRY IN THE INITIALIZED DATA SLOT */
		  if(SymFindRange(pf->datatbl, &kp->spot, &dp))
		  {/* This entry describes a block of data containg the reloc target */
		  unsigned char *ip = dp->p;	/* points to input buffer */
		  unsigned long extra = kp->spot - dp->offset; /* offset into data */


			/* Reset the relocop target in the input file */

			PL( POPI->reloc.spot ) = GL( ((PopI)(ip+8))->s.offset ) + extra;
			if(noset)
				continue;

			if(kp->rsize == 4)
			{/* 32 bit relocation */
			unsigned long *lp;

				lp = (unsigned long*)(ip+24+extra);	/* pointer to target */
				object_base = GL( POPI->reloc.base );

				/* Find the object that the target points to */
relink32:
				if(SymFindRange(npf->datatbl, &object_base, &ndp))
				{
					if(*(ndp->p) == 0)
					{/* The found object is a discarded thunkblock, relink */
						npf = iv->files[GS( ((short*)(ndp->p))[1] )];
						object_base = GL( ((Pop)(ndp->p))->data4 );
						goto relink32;
					}
					else
					{/* Use the new offset in the input file */

						object_base = GL( ((Pop)(ndp->p))->data1 );
					}
					PL( POPI->reloc.base ) = object_base; /* the `relocop' */
					PL(*lp) = object_base + GL( POPI->reloc.offset );/* data */
					vp->base = lp;
				}
				else
				{
					++iv->errors;
					PWARN(pName ":syserr: 32 bit object at offset %d not found\n",object_base);
				}
			}
			else if(kp->rsize == 2)
			{/* 16 bit relocation (MORE WORK NEEDED) */
			unsigned short *sp;

				sp = (unsigned short*)(ip+24+extra);	/* pointer to target */
				object_base = GL( POPI->reloc.base );
relink16:
				if(SymFindRange(npf->datatbl, &object_base, &ndp))
				{
					if(*(ndp->p) == 0)
					{/* The found object is a discarded thunkblock, relink */
						npf = iv->files[GS( ((short*)(ndp->p))[1] )];
						object_base = GL( ((Pop)(ndp->p))->data4 );
						goto relink16;
					}
					else
					{/* Use the new offset in the input file */

						object_base = GL( ((Pop)(ndp->p))->data1 );
					}
					PL( POPI->reloc.base ) = object_base; /* the `relocop' */
					PS(*sp) = object_base + GL( POPI->reloc.offset );/* data */
				}
				else
				{
					++iv->errors;
					PWARN(pName ":syserr: 16 bit object at offset %d not found\n", object_base);
				}

			}
		  }
		  else /* !SymFindRange */
		  {
			++iv->errors;
			PWARN(pName ":syserr: reloc not found at %d in file %d\n", 
					kp->spot, kp->fileno);
		  }
		}/* END: While(SymNext) */
	}/* END: if(SymHead) */
}
static void
reset_offset(Piv iv, Pafile pf, PopA pa)
{/* All offsets are guaranteed to be inside objects */
struct _data {/* datatbl node */
/* value area 16 bytes */
	unsigned long size;
	unsigned char *p;
	unsigned long unused[2];
/* key area 8 bytes */
	unsigned long offset;
	long unused1;
};

unsigned long offset;
struct _data *dp;
unsigned long object_base;
long extra;
unsigned short atype;
short symnum;

	offset = GL( pa->offset );
	atype = GS( pa->atype );
	symnum = GS( pa->symnum );

	PS( pa->symnum ) = pf->symtran[symnum];
	PS( pa->declnum ) = pf->decltran[GS(pa->declnum)];

	if(atype & A_EXTERN)
	{
	short key[4];
	struct {
	unsigned char *p;	/* pointer to extvarop in input buffer */
	} *ep;
		key[0] = symnum; 		/* external symbol number */
		key[1] = pf->filenum;
		key[2] = 0;
		key[3] = 0;

			/* LOOK UP THE EXTERNAL SYMBOL */
		if(!SymFind(iv->extrntbl, key, &ep) && *(ep->p))
		{/* symbol exists and the extvarop was filled in */

			pf = iv->files[GS( ((short*)(ep->p))[1] )];
			offset += GL( ((Pop)(ep->p))->data1 );
		}
		else
		{/* Not found or not filled in, leave it alone */

			return;
		}
	}
	extra = 0;	/* first time through */

	/* Find the object that the offset points to */
relink:
	if(SymFindRange(pf->datatbl, &offset, &dp))
	{
		if(extra == 0)
			extra = offset - dp->offset;
		object_base = dp->offset;

		if(*(dp->p) == 0)
		{/* The found object is a discarded block, relink */
			pf = iv->files[GS( ((short*)(dp->p))[1] )];
			offset = GL( ((Pop)(dp->p))->data4 );
			goto relink;
		}
		else
		{/* Use the adjusted offset in the input buffer */

			object_base = GL( ((Pop)(dp->p))->data1 );
		}
		PL( pa->offset ) = object_base + extra;
		if(atype & A_EXTERN)
		{
			PS( pa->atype ) = atype & ~A_EXTERN;		
		}
	}
	else
	{
		++iv->errors;
		PWARN(pName ":syserr: object `%s' at offset %d not found\n", 
			pf->symaddr[symnum], offset);
	}
}
static void
reset_text_relocs(Piv iv)
{/* Pass over text and set new offsets in instructions that reference data */
int i;

	for(i = 0; i < iv->numfiles; ++i)
	{
	Pafile pf;
	unsigned char *p;

		pf = iv->files[i];
		if(!(p = pf->prog_p))
			continue;

		while(*p != endfileop)
		{
			if(*p && *p <= (unsigned char)100)
			{/* instruction */
			int inc = 8;
				if((p[1]&0xe0) == OPDATA)
					reset_offset(iv, pf, POPA);
				inc += (p[1]&0x1f);
				if((p[2]&0xe0) == OPDATA)
					reset_offset(iv, pf, POPA);
				inc += (p[2]&0x1f);
				if((p[3]&0xe0) == OPDATA)
					reset_offset(iv, pf, POPA);
			}
			p = POP->next;				
		}
	}

}
static void *
seg_find(Piv iv, int id)
{
long key[2];
void **result;

	if(iv->segtbl)
	{
		key[0] = id;
		key[1] = 0;
		if(SymFind(iv->segtbl, key, &result))
			return *result;
	}
	return 0;	
}
static void
check_seg(Piv iv, unsigned char *p, Pafile pf)
{
PopI np, op;
	if(!(iv->segtbl))
	{
		iv->segtbl = NewSymTable(iv->category, 111);
	}
	if((op = seg_find(iv, GS(POPI->segdef.segid))))
	{
		np = POPI;
		if(		GL(np->segdef.v1) == GL(op->segdef.v1)
			&&	GL(np->segdef.v2) == GL(op->segdef.v2)
			&&	GL(np->segdef.v3) == GL(op->segdef.v3))
		{/* segments of same name have the same values */
			*p = 0;	/* kill the new definition */
			return;
		}
		else
		{/* segments of same name have different values */
			++iv->errors;
			PWARN(pName ":Segment `%s' defined differently.\n",
				iv->symaddr[GS(POPI->segdef.segid)]);
			PWARN(pName ":  In file: `%s'\n", pf->symaddr[INFILE_SYMNUM]);
			return;
		}
	}
	else
	{
	long key[2];
	PopI pp = POPI;
		key[0] = GS(POPI->segdef.segid);
		key[1] = 0;
		SymInsert(iv->segtbl, key, &pp, 4);
	}
}
static void
reset_syms_decls(Piv iv)
{
int i;
	for(i = 0; i < iv->numfiles; ++i)
	{
	Pafile pf;
	unsigned char *p;

		pf = iv->files[i];
		p = pf->file_p;

		while(*p != endfileop)
		{
		  if(*p == segdefop)
		  {
			PS(POPI->segdef.segid) = pf->symtran[GS(POPI->segdef.segid)];
			check_seg(iv, p, pf);
		  }
		  else if(i > 0)
		  {
			switch(*p)
			{
				case	declop:
					if(GS(POPI->dcl.declnum) < 22)
					{/* kill the base declarations */
						*p = 0;
						p = POP->next;				
						*p = 0;
					}
					else
					 PS(POPI->dcl.declnum)=pf->decltran[GS(POPI->dcl.declnum)];
					break;
				case	extlocop:
					PS(POPI->reloc.rsym) = pf->symtran[GS(POPI->reloc.rsym)];
					break;
				case	gfuncdefop:
				case	sfuncdefop:
					if(pf->numsegs)
					PS(POPI->funcdef.segid) = pf->symtran[GS(POPI->funcdef.segid)];
				case	nestedfuncdefop:
					PL(POPI->funcdef.symnum) = pf->symtran[GL(POPI->funcdef.symnum)];
					break;
				case	bssblockop:
				case	datablockop:
					if(pf->numsegs)
					PS( POPI->s.segid ) = pf->symtran[GS(POPI->s.segid)];
				case	stringblockop:
				case	mallocblockop:
				case	thunkblockop:
				case	extvarop:
					PS( POPI->s.symnum ) = pf->symtran[GS(POPI->s.symnum)];
					PS( POPI->s.declnum ) = pf->decltran[GS(POPI->s.declnum)];
					break;
				case	memberinfop:
				case	bfieldinfop:
					PS(POPI->memb.symnum) = pf->symtran[GS(POPI->memb.symnum)];
					PS(POPI->memb.declnum) = pf->decltran[GS(POPI->memb.declnum)];
					PS(POPI->memb.cdeclnum) = pf->decltran[GS(POPI->memb.cdeclnum)];
					break;
				case	structinfop:
					PS(POPI->suinf.symnum) = pf->symtran[GS(POPI->suinf.symnum)];
					break;
				case	funcptrinfop:
				case	ptrinfop:
					PS(POPI->ptrinf.declnum) = pf->decltran[GS(POPI->ptrinf.declnum)];
					break;
				case	funcinfop:
					PS(POPI->funcd.declnum) = pf->decltran[GS(POPI->funcd.declnum)];
					PS(POPI->funcd.symnum) = pf->symtran[GS(POPI->funcd.symnum)];
					break;
				case	arrayinfop:
					PS(POPI->ary.declnum) = pf->decltran[GS(POPI->ary.declnum)];
					break;
				case	lineop:
					PL(POPI->line.filenamenum) = pf->symtran[GL(POPI->line.filenamenum)];
					break;
			}/* END: switch(*p) */
		  }/* END: i > 0 */
		  p = POP->next;				
		}
	}
}
static int
link_files(Piv iv)
{
	iv->extrntbl = NewSymTable(iv->category, 4092);	/* hashed table */
	iv->reloctbl = NewSymTable(iv->category, 4092); /* hashed table */
	iv->gbltbl = NewSymTable(iv->category, 0);	/* sorted table */

	setup_syms_decls(iv);

	if(iv->numfiles > 1)
	{
		iv->symaddr = Ccalloc(iv->category, sizeof(void*), iv->numsyms+1);
		iv->decladdr = Ccalloc(iv->category, sizeof(void*), iv->numdecls+1);
		iv->symtbl = NewSymTable(iv->category, 0); /* sorted table */
		combine_syms_decls(iv);

		link_globals(iv);
		realloc_data(iv);
		reset_data_relocs(iv);
		reset_text_relocs(iv);

		reset_syms_decls(iv);
	}
	else
	{
		iv->symaddr = iv->files[0]->symaddr;
		iv->decladdr = iv->files[0]->decladdr;
		iv->symtbl = NewSymTable(iv->category, 0); /* sorted table */
		combine_syms_decls(iv);

		realloc_data(iv);
		reset_data_relocs(iv);
		reset_text_relocs(iv);
	}
	return iv->errors;
}

/* ======================== GLOBAL ROUTINES ========================== */
int
Global(readfile) (Piv iv, char *infile_name)
{
FILE *infile;
long infile_size;
char *inbuf;

	if(!(infile = fopen(infile_name, "rb")))
	{
		PERROR(pName ":ERROR: Can't open input file: %s\n", infile_name);
	}
	fseek(infile, 0, SEEK_END);
	infile_size = ftell(infile);	
	fseek(infile, 0, SEEK_SET);

	if(infile_size == 0)
	{
		PERROR(pName ":ERROR: Empty input file: %s\n", infile_name);
	}
	inbuf = Cmalloc(iv->category, infile_size);

	if(fread(inbuf, 1, infile_size, infile) != infile_size)
	{
		fclose(infile);
		PERROR(pName ":ERROR: reading input file: %s\n", infile_name);
	}
	fclose(infile);

	if(setup_nodelinks(iv, infile_name, inbuf, infile_size))
		return 4;
	return 0;
}
int
Global(proc_files) (Piv iv, void *name)
{
int ret;

	if(!(ret = link_files(iv)))
	{
		optimize(iv);
		if(name)
		  iv->symaddr[2] = name;	/* symbol 2 is the output filename */

		ret = gen_output(iv, iv->symaddr[2]);
	}
	return ret;
}
void *
Global(open_instance) (void)
{
Piv iv;
int category;
#if USING_FRAMEWORK
	if(num_instance <= 0)
	{
		oxlink_clear_bss(pName ".o");	/* reset global storage */
		local_category = NewMallocCategory();
	}
	++num_instance;
#endif
	category = Cnewcat();
	iv = Ccalloc(category, 1, sizeof(struct _iv));
	iv->category = category;
	iv->finextbuf = (PEL)&iv->finextbufstart;
	return iv;
}
void
Global(close_instance) (Piv iv)
{
	if(iv->outfile)
	  fclose(iv->outfile);
	if(iv->remove_infile)
	{
	int i;
	  for(i = 1; i < iv->argc; ++i)
		unlink(propernameof(iv, iv->argv[i]));
	}
	Cfreecat(iv->category);
#if USING_FRAMEWORK
	if(--num_instance == 0)
		freecat(local_category);
#endif
}

/* =========================== THE MAIN PROGRAM ======================= */

static char *
filenameof(char *path)
{
char *ret = path;
int i;

	for(i = 0; path[i]; ++i)
	  if(path[i] == '/')
		ret = &path[i+1];
	return ret;
}

static char *
propernameof(Piv iv, char *path)
{
char *name = filenameof(path);
int namlen = strlen(name);
int i;
	for(i = namlen-1; i >= 0; --i)
	{
	  if(name[i] == '/' || name[i] == '\\')
	  	break;
	  else if(name[i] == '.')
		return path;
	}
	name = Cmalloc(iv->category, strlen(path)+8);
	strcpy(name, path);
	strcat(name, ".anf");
	return name;
}
static void
Usage()
{
fputs(
"Usage: " pName " [-odsDLR?] [infile...]\n"
"   -o outfile == name of output file\n"
"   -d == print debug output\n"
"   -D == only print debug output\n"
"   -s == strip declarations and line numbers\n"
"   -L == generate listing only (to .lst)\n"
"   -R == remove input file\n"
"   -? == print this message\n"
"   Default input file is `code.anf'.\n"
"   Default output file is specified by the input.\n"
,stderr);
}

#if USING_FRAMEWORK
int
PROG (int argc, char **argv)
#else
int
main (int argc, char **argv)
#endif
{
int i,j;
char *outfilename = 0;
volatile Piv iv;
char debug, only_debug, strip, listing_wanted, remove_infile;
int ret;

	remove_infile = listing_wanted = strip = debug = only_debug = 0;

	/* Get options */
	for(i = 1; i < argc; ++i)
	{
	int trimsize = 1;

		if(argv[i][0] == '-')
		{
			for (j=1; argv[i][j]; j++)
			{
				switch(argv[i][j])
				{	
					case	'D':
						only_debug = 1;
						/* FALL THROUGH */
					case	'd':
						debug = argv[i][++j];
						break;
					case	's':
						strip = 1;
						break;
					case	'o':
						if(argv[i][j+1]) {
							outfilename = &argv[i][j+1];
						}
						else if(i < argc-1) {
							outfilename = argv[i+1];
							trimsize = 2;
						} else {
							PWARN(pName ": no output filename\n");
							Usage();
							return 0;
						}
						goto trim;
						break;
					case	'L':
						listing_wanted = 1;
						break;
					case 'R':
						remove_infile = 1;
						break;
					case '?':
						Usage();
						return 0;
					default:
						PWARN(pName ": Invalid switch: 0x%x\n", argv[i][j]);
						Usage();
						return 0;
				}
			}/* END: for(j) */
trim:
			/* Trim switch */
			for(j = i; j < argc-trimsize; ++j)
				argv[j] = argv[j+trimsize];
			argc -= trimsize;
			--i;
		}/* END: if('-') */
	}/* END: for(argc) */

#if 0
oxcc_debug(__builtin_iv(),0x40000);
#endif

	iv = Global(open_instance) ();
	if((ret = setjmp(run_env))) {
		Global(close_instance) (iv);
#if USING_FRAMEWORK
		return ret;
#else
		exit(ret);
#endif
	}
	iv->debug = debug;
	iv->only_debug = only_debug;
	iv->labeltbl = NewSymTable(iv->category, 4092);
#if REALLY_NEED_OFFSETS
	iv->newlabeltbl = NewSymTable(iv->category, 4092);
#endif
	iv->strip = strip;
	iv->listing_wanted = listing_wanted;
	iv->remove_infile = remove_infile;
	iv->argc = argc;
	iv->argv = argv;

	if(argc < 2)
	{/* Default input filename is 'code.anf' */
		ret = Global(readfile) (iv, "code.anf");
	}
	else
	{/* READ EACH INPUT FILE */
		for(i = 1; i < argc; ++i)
		  if((ret = Global(readfile) (iv, propernameof(iv,argv[i]))))
			break;
	}
	if(!ret && !iv->only_debug)
	{
		ret = Global(proc_files) (iv, outfilename);
	}
	Global(close_instance) (iv);
#if USING_FRAMEWORK
	return ret;
#else
	exit(ret);
#endif
}

