Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kcc-5/kcc/ccout.c
There are 8 other files named ccout.c in the archive. Click here to see a list.
/*	CCOUT.C - Output pseudo-code to assembler file
**
**	All changes after version 77 (8/8/85), unless otherwise specified, are
**	Copyright 1985, 1986 by Ken Harrenstien, SRI International.
*/
/* [SRI-NIC]SS:<C.KCC.CC>CCOUT.C.102, 17-Dec-85 08:00:15, Edit by KLH */
/*  Rationalized names of constants and structures */
/* [SRI-NIC]SS:<C.KCC.CC>CCOUT.C.84, 13-Dec-85 11:01:50, Edit by KLH */
/*  Change smallfloat to bigfloat, fix up for use of pfloat */
/* <KCC.CC>CCOUT.C.73,  7-Jul-85 12:35:41, Edit by KRONJ */
/*  Fix some strange errors */
/* <KCC.CC>CCOUT.C.70,  5-Jul-85 15:13:02, Edit by KRONJ */
/*  safe to do TRNA fold when next op always skips (as well as JRST, POPJ) */
/* <KCC.CC>CCOUT.C.68, 27-Jun-85 23:20:04, Edit by WHP4 */
/*  don't generate lines like:  MOVEM 5,16   EXCH 4,15 in DFIX R,R stuff */
/* <KCC.CC>CCOUT.C.65, 20-Jun-85 17:51:38, Edit by KRONJ */
/*  Use $ZERO for DFLOT, fix DFIX 14,14 */
/* <KCC.CC>CCOUT.C.60, 11-Jun-85 11:00:41, Edit by KRONJ */
/*  Improve emission for left-half-only bitwise ops */
/* <KCC.CC>CCOUT.C.58,  7-Jun-85 14:33:17, Edit by KRONJ */
/*  Gross $$ONE hack to avoid FAIL wrong segment relocation bug */
/* <KCC.CC>CCOUT.C.53,  4-Jun-85 11:19:17, Edit by KRONJ */
/*  Copy preamble from header file */
/* <KCC.CC>CCOUT.C.49, 31-May-85 10:44:36, Edit by KRONJ */
/*  Try to eliminate TRNAs */
/* <KCC.CC>CCOUT.C.48, 29-May-85 10:52:02, Edit by KRONJ */
/*  pptr is now always a (SYMBOL *) */
/* <KCC.CC>CCOUT.C.46, 29-May-85 10:11:29, Edit by KRONJ */
/*  Flush outlab() */
/* <KCC.CC>CCOUT.C.42, 21-May-85 11:53:45, Edit by KRONJ */
/*  Inline DFLOT */
/* <KCC.CC>CCOUT.C.38, 15-Mar-85 11:54:18, Edit by KRONJ */
/*  New reentrant calling convs for sim routines */
/* <KCC.CC>CCOUT.C.34, 10-Mar-85 11:00:37, Edit by KRONJ */
/*  If we run LINK, make it save EXE */
/* <KCC.CC>CCOUT.C.32, 10-Mar-85 07:39:12, Edit by KRONJ */
/*  No more BLABEL */
/* <KCC.CC>CCOUT.C.29, 27-Feb-85 13:52:12, Edit by SATZ */
/*  Change .START and ..STRT to $START and $$STRT */
/* <KCC.CC>CCOUT.C.28, 24-Feb-85 01:32:59, Edit by SATZ */
/*  Symbol table is a linked list instead of an array */
/* <KCC.CC>CCOUT.C.27, 17-Feb-85 11:05:49, Edit by SATZ */
/*  Make sure main() is an internal symbol before making entry vector */
/* <KCC.CC>CCOUT.C.26,  5-Feb-85 18:25:26, Edit by SATZ */
/*  Make sure some MACRO/FAIL symbols are PURGED */
/* <KCC.CC>CCOUT.C.24, 27-Jan-85 17:32:44, Edit by SATZ */
/*  Modify postamble to output END with a starting address */
/*  and a label with a JRST to the real starting address so */
/*  the SAVE command after loading a C program will use that */
/*  program's name */
/* <KCC.CC>CCOUT.C.20,  3-Jan-85 23:33:38, Edit by KRONJ */
/*  change calling conv for SPUSH/SPOP to JSP */
/* <KCC.CC>CCOUT.C.18,  3-Jan-85 17:26:19, Edit by KRONJ */
/*  SPUSH and SPOP */
/* SCORE:<KCC.CC>CCOUT.C.10, 25-Jul-84 16:16:02, Edit by KRONJ */
/*  FDVR can also be folded into FSC */
/* SCORE:<KCC.CC>CCOUT.C.7, 24-Jul-84 16:16:04, Edit by KRONJ */
/*  improve smallfloat() */
/* SCORE:<KCC.CC>CCOUT.C.5, 24-Jul-84 13:13:17, Edit by KRONJ */
/*  move some routines here from ccgen */
/* SCORE:<KCC.CC>CCOUT.C.3, 22-Jul-84 18:08:54, Edit by KRONJ */
/*  fold multiply by power of two into ASH */
/* SCORE:<KCC.CC>CC8A.C.63, 29-Jun-84 17:18:09, Edit by KRONJ */
/*  IFIW is another always-big op */
/* SCORE:<KCC.CC>CC8A.C.57,  9-Jun-84 20:35:56, Edit by KRONJ */
/*  flushed indirect types - too much potential for lossage */
/* SCORE:<KCC.CC>CC8A.C.12, 14-May-84 13:39:22, Edit by KRONJ */
/*  glabel() moves here */
/* SCORE:<KCC.CC>CC8A.C.11, 13-May-84 22:58:30, Edit by KRONJ */
/*  Set outputted ops to CODED for before()!  Why wasn't this done before??? */
/* SCORE:<KCC.CC>CC8A.C.2,  5-May-84 15:52:28, Edit by KRONJ */
/*  Special case for logical not generation */

    /*
    ** ccout.c -- emit assembly language text
    **            (C) 1981  K. Chen
    */

#include "cc.h"
#include "ccgen.h"
#include "ccchar.h"

/* Imported functions */
extern SYMBOL *findsym();	/* CCSYM */


/* Exported functions (defined here and used externally) */
void initout(), realcode(), postamble(), codeseg(), dataseg(),
	outlab(), outscon(), outmpdbl(),
	outnum(), outc(), outstr(), outnl(), outtab(), outid(), outptr();
int binexp();			/* CCFOLD */
int makprefile();

/* Functions local to this module */
static void simptrcnv(), simsmove(), simuidiv(),
	simsubbp(), simfltr(), simdfix();
static void outpreamble(), outop(), outreg(), oreg(), outrmem(),
	outpti(),
	outpos(), outaddress(), outasmh();
static int fltpow2(), outdecl(), bigfloat(), obplh();

static whichseg;	/* 0/no preamble, 1/code, -1/data */
/* Tables to handle references to built-in C RunTime symbols without
** going through the overhead of using the symbol table.
** Currently this is only used to determine which CRT symbols need to
** be declared EXTERN in the assembly-language output file.
** Note that $$CVER is always defined and declared in every KCC module,
** as are the appropriate $$CPxx symbols.
*/
#define crtsyms \
	crtsym(CRT_,"")			/* avoid zero index */\
	crtsym(CRT_CRT, "$$$CRT")	/* CRT module "entry point" */\
	crtsym(CRT_CPU, "$$$CPU")	/* CPU module "entry point" */\
	crtsym(CRT_CVER,  "$$CVER")	/* KCC code & library version # */\
	crtsym(CRT_CPUKA, "$$CPKA")	/* KCC/CPU type KA-10 */\
	crtsym(CRT_CPUKI, "$$CPKI")	/* KCC/CPU type KI-10 */\
	crtsym(CRT_CPUKS, "$$CPKS")	/* KCC/CPU type KS-10 and KL-10A */\
	crtsym(CRT_CPUKL0,"$$CPKL")	/* KCC/CPU type KL-10B, section 0 */\
	crtsym(CRT_CPUKLX,"$$CPKX")	/* KCC/CPU type KL-10B, non-0 sect */\
	crtsym(CRT_SECT, "$$SECT")	/* CPU Section # being loaded into */\
	crtsym(CRT_BPH0, "$$BPH0")	/* CPU Byte ptr LHs for 18-bit bytes*/\
	crtsym(CRT_BPH1, "$$BPH1")	/* CPU */\
	crtsym(CRT_BP90, "$$BP90")	/* CPU Byte ptr LHs for 9-bit bytes */\
	crtsym(CRT_BP91, "$$BP91")	/* CPU */\
	crtsym(CRT_BP92, "$$BP92")	/* CPU */\
	crtsym(CRT_BP93, "$$BP93")	/* CPU */\
	crtsym(CRT_BP80, "$$BP80")	/* CPU Byte ptr LHs for 8-bit bytes */\
	crtsym(CRT_BP81, "$$BP81")	/* CPU */\
	crtsym(CRT_BP82, "$$BP82")	/* CPU */\
	crtsym(CRT_BP83, "$$BP83")	/* CPU */\
	crtsym(CRT_BP70, "$$BP70")	/* CPU Byte ptr LHs for 7-bit bytes */\
	crtsym(CRT_BP71, "$$BP71")	/* CPU */\
	crtsym(CRT_BP72, "$$BP72")	/* CPU */\
	crtsym(CRT_BP73, "$$BP73")	/* CPU */\
	crtsym(CRT_BP74, "$$BP74")	/* CPU */\
	crtsym(CRT_BP60, "$$BP60")	/* CPU Byte ptr LHs for 6-bit bytes */\
	crtsym(CRT_BP61, "$$BP61")	/* CPU */\
	crtsym(CRT_BP62, "$$BP62")	/* CPU */\
	crtsym(CRT_BP63, "$$BP63")	/* CPU */\
	crtsym(CRT_BP64, "$$BP64")	/* CPU */\
	crtsym(CRT_BP65, "$$BP65")	/* CPU */\
	crtsym(CRT_BPPS, "$$BPPS")	/* CPU Mask for BP P+S field */\
	crtsym(CRT_BPSZ, "$$BPSZ")	/* CPU BP LH to get BP size field */\
	crtsym(CRT_BSHF, "$$BSHF")	/* CPU # bits to shift in P_SUBBP */\
	crtsym(CRT_BMP6, "$$BMP6")	/* CPU val for 6bit MULI in P_SUBBP */\
	crtsym(CRT_BMP7, "$$BMP7")	/* CPU  "   "  7-bit " */\
	crtsym(CRT_BMP8, "$$BMP8")	/* CPU  "   "  8-bit " */\
	crtsym(CRT_BMP9, "$$BMP9")	/* CPU  "   "  9-bit " */\
	crtsym(CRT_BMPH, "$$BMPH")	/* CPU  "   "  18-bit " */\
	crtsym(CRT_PH90, "$$PH90")	/* CPU Instr #0 for 18->9 cnv */\
	crtsym(CRT_PH91, "$$PH91")	/* CPU Instr #1 for 18->9 cnv */\
	crtsym(CRT_P9H0, "$$P9H0")	/* CPU Instr #0 for 9->18 cnv */\
	crtsym(CRT_P9H1, "$$P9H1")	/* CPU Instr #1 for 9->18 cnv */\
	crtsym(CRT_P9H2, "$$P9H2")	/* CPU Instr #2 for 9->18 cnv */\
	crtsym(CRT_START,"$START")	/* CRT start location */\
	crtsym(CRT_RET,  "$RET")	/* CRT Convenient return label */\
	crtsym(CRT_RETZ, "$RETZ")	/* CRT    ditto, returns 0 */\
	crtsym(CRT_RETF, "$RETF")	/* CRT    ditto, returns 0 */\
	crtsym(CRT_RETP, "$RETP")	/* CRT    ditto, returns 1 */\
	crtsym(CRT_RETT, "$RETT")	/* CRT    ditto, returns 1 */\
	crtsym(CRT_RETN, "$RETN")	/* CRT    ditto, returns -1 */\
	crtsym(CRT_ZERO, "$ZERO")	/* CRT Double 0.0 constant */\
	crtsym(CRT_ADJBP, "$ADJBP")	/* CRT ADJBP simulation rtn (unused)*/\
	crtsym(CRT_BPMUL, "$BPMUL")	/* CRT BP mul table (for P_SUBBP) */\
	crtsym(CRT_BPADT, "$BPADT")	/* CRT BP table of $BPADn (for ") */\
	crtsym(CRT_BPAD6, "$BPAD6")	/* CRT BP 6-bit add table (for ") */\
	crtsym(CRT_BPAD7, "$BPAD7")	/* CRT BP 7-bit add table (for ") */\
	crtsym(CRT_BPAD8, "$BPAD8")	/* CRT BP 8-bit add table (for ") */\
	crtsym(CRT_BPAD9, "$BPAD9")	/* CRT BP 9-bit add table (for ") */\
	crtsym(CRT_BPADH, "$BPADH")	/* CRT BP 18-bit add table (for ") */\
	crtsym(CRT_BPCNT, "$BPCNT")	/* CRT Byte ptr auxiliary */\
	crtsym(CRT_SUBBP, "$SUBBP")	/* CRT Byte ptr subtraction aux */\
	crtsym(CRT_DFIX,  "$DFIX")	/* CRT Aux for (int)(double) cast */\
	crtsym(CRT_DFIXS, "$DFIXS")	/* CRT Aux for (int)(double) cast */\
	crtsym(CRT_DFLTS, "$DFLTS")	/* CRT Aux for (double)(int) cast */\
	crtsym(CRT_SPUSH, "$NSPUSH")	/* CRT Aux to put struct on stack */\
	crtsym(CRT_SPOP,  "$NSPOP")	/* CRT Aux to get struct from stack */

#define crtsym(idx, sym) idx,
enum { crtsyms CRT_N };		/* Define the CRT_ indices plus count */
#undef crtsym

static int crtref[CRT_N];		/* Table of CRT sym reference counts */
#define crtsym(idx, sym) sym,
static char *crtsnam[CRT_N] = { crtsyms };	/* Table of CRT symbol names */
#undef crtsym
/*
** INITOUT - Initialize code output
**	Called once for each file compiled.
*/
void
initout()
{
    register int i;

    for(i = CRT_N; --i >= 0;)	/* Reset reference counts */
	crtref[i] = 0;

    /* These two runtime symbols are ALWAYS implicitly referenced;
    ** they refer to the CRT and CPU runtime modules.
    ** See postamble() for a special reset check, though.
    */
    ++crtref[CRT_CRT];
    ++crtref[CRT_CPU];

    whichseg = 0;			/* No preamble done yet */
}
/* ---------------------------- */
/*      generate real code      */
/* ---------------------------- */
void
realcode(n)
{
    PCODE *p, *q, *a;
    char *op, *t;
    int opr, typ, i, *fptr;

    p = &codes[n&(MAXCODE-1)];

    typ = (p->Ptype &~ PTF_SKIPPED);

    switch ((opr = p->Pop) & POF_OPCODE) {

    case P_NOP:			/* Ignore deleted pseudo-code */
	return;

    /* Handle the various simulated ops, which do not correspond
    ** directly to a real PDP-10 instruction.
    */
    case P_PTRCNV:		/* "Pointer Conversion" instruction */
	simptrcnv(p);		/* Go handle it. */
	return;

    case P_SMOVE:		/* "Structure Move" instruction */
	simsmove(p);
	return;

    case P_UIDIV:		/* "Unsigned Integer Divide" instruction */
	simuidiv(p);
	return;

    case P_SUBBP:		/* "Subtract Byte Pointer" instruction */
	simsubbp(p);
	return;

    case P_DFIX:		/* "Double Fix" instruction */
	simdfix(p);
	return;

    case P_FLTR:		/* "Float and Round" may need simulation */
	if (!tgmachuse.fixflt) {	/* Simulate FLTR R,M ? */
	    simfltr(p);			/* Yes, go simulate. */
	    return;
	}
	break;

    /* End of simulated ops; switch continued on next page! */
    /* Switch continued from previous page!  Start of "real" ops. */

    /*
    ** If we are doing a comparison with a number, and the number
    ** is larger than 18 bits, we have to use a literal.  That
    ** entails changing the opcode from P_CAIE etc to P_CAME...
    **
    ** We also have to perform a similar transformation if we want
    ** to compare the register with an immediate address.
    ** Luckily that can never happen in the second of a cascaded skip.
    */
    case P_TRN:
	if (foldtrna (p)) return;
    case P_TRC:
    case P_TRZ:
    case P_TRO:
    case P_CAI:
	if (typ == PTA_MINDEXED) {
	    outstr("\tXMOVEI");		/* turn immediate compare of addr */
	    outreg(R_SCRREG);		/* int XMOVEI+compare for extended */
	    outaddress(p);		/* addressing. */
	    outnl();
	    typ = PTA_REGIS;		/* CAIN R,addr becomes */
	    p->Pr2 = R_SCRREG;		/* CAMN R,16 */
	    opr = directop(opr);
	    break;
	}

	if (typ != PTA_RCONST || (p->Pvalue &~ 0777777) == 0) break;
	if ((p->Pvalue & 0777777) == 0)
	    switch (opr & POF_OPCODE) {
		case P_TRN:
		    opr ^= P_TRN ^ P_TLN;
		    p->Pvalue = ((unsigned) p->Pvalue) >> 18;
		    break;
		case P_TRO:
		    opr ^= P_TRO ^ P_TLO;
		    p->Pvalue = ((unsigned) p->Pvalue) >> 18;
		    break;
		case P_TRZ:
		    opr ^= P_TRZ ^ P_TLZ;
		    p->Pvalue = ((unsigned) p->Pvalue) >> 18;
		    break;
		case P_TRC:
		    opr ^= P_TRC ^ P_TLC;
		    p->Pvalue = ((unsigned) p->Pvalue) >> 18;
		    break;
	    }
	if (!(p->Pvalue &~ 0777777)) break; /* still too big? */
	opr = directop(opr);	/* Turn into memory version */
	typ |= PTF_IMM;		/* Force outrmem to discover bigness */
	break;

    /* Special check for TDZ with a pointer operand, which is almost
    ** certainly masking out the P+S fields of a pointer (to convert it
    ** to a word pointer)
    */
    case P_TDZ:
	if (typ == PTA_PCONST && p->Pptr == NULL && p->Poffset == 0) {
	    p->Pop ^= P_TDZ ^ P_TLZ;	/* Change op from TDZ to TLZ */
	    outpti(p);			/* Output the instr */
	    return;
	}
	break;

    /* Similar check for TDO with a pointer-constant operand, since
    ** IOR is sometimes converted into a TDO.  All we do here is
    ** change it to a TLO if that's possible.
    */
    case P_TDO:
	if (typ == PTA_PCONST && p->Pptr == NULL && p->Poffset == 0) {
	    p->Pop ^= P_TDO ^ P_TLO;	/* Change op from TDO to TLO */
	    outpti(p);			/* Output the instruction */
	    return;
	}
	break;

    /*
    ** Non-test bitwise operations
    ** We try turning these into tests to save a memory reference
    ** from a literal constant.
    */
    case P_IOR:
	if (typ == PTA_PCONST && p->Pptr == NULL) {	/* Hacking BPs? */
	    p->Pop ^= P_IOR ^ P_TLO;	/* Change op to TLO */
	    outpti(p);			/* Output the instr */
	    return;
	}
	if (typ != PTV_IMMED || (p->Pvalue &~ 0777777) == 0) break;
	if ((p->Pvalue & 0777777) == 0) { /* x,,0 */
	    opr = p->Pop = P_TLO;
	    typ = PTA_RCONST;
	    p->Pvalue = ((unsigned) p->Pvalue) >> 18;
	} else if (((unsigned) p->Pvalue >> 18) == 0777777) { /* 777777,,x */
	    opr = p->Pop = P_ORCM;
	    p->Pvalue = (~p->Pvalue) & 0777777;
	}
	break;

    case P_XOR:
	if (typ != PTV_IMMED || (p->Pvalue &~ 0777777) == 0) break;
	if ((p->Pvalue & 0777777) == 0) { /* x,,0 */
	    opr = p->Pop = P_TLC;
	    typ = PTA_RCONST;
	    p->Pvalue = ((unsigned) p->Pvalue) >> 18;
	} else if (((unsigned) p->Pvalue >> 18) == 0777777) { /* 777777,,x */
	    opr = p->Pop = P_EQV;
	    p->Pvalue = (~p->Pvalue) & 0777777;
	}
	break;

    case P_AND:
	if (typ != PTV_IMMED || (p->Pvalue &~ 0777777) == 0) break;
	if ((p->Pvalue & 0777777) == 0777777) { /* x,,777777 */
	    opr = p->Pop = P_TLZ;
	    typ = PTA_RCONST;
	    p->Pvalue = (unsigned)(~p->Pvalue) >> 18;
	} else if (((unsigned)p->Pvalue >> 18) == 0777777) { /* 777777,,x */
	    opr = p->Pop = P_TRZ;
	    typ = PTA_RCONST;
	    p->Pvalue = (~p->Pvalue) & 0777777;
	}
	break;
    /*
    ** Signed integer multiplication by a power of two is better done as an ASH
    ** (arithmetic shift - not the same as LSH).  Don't try this
    ** for division, as the result is incorrect for negative numbers!
    ** Note that unsigned multiplication will use P_MUL, not P_IMUL.
    ** Note the subtle test for power-of-two-ness which relies on
    ** twos-complement arithmetic.
    **
    ** Similarly, floating multiply by two becomes FSC (floating scale).
    ** Division is OK, unlike the integer case.
    ** This can only work for single-precision floats (not doubles).
    ** Otherwise we fall through to the other floating code.
    */

    case P_IMUL:
	if (typ == PTV_IMMED && (p->Pvalue & (p->Pvalue - 1)) == 0 && optobj) {
	    p->Pvalue = binexp(p->Pvalue);	/* get # of bits to shift */
	    p->Pop = opr = P_ASH;		/* and opcode */
	    typ = PTA_RCONST;			/* and code type */
	}
	break;

    case P_FMPR:		/* If doing single-precision mult or div */
    case P_FDVR:
	if (typ == PTA_FCONST		/* If operand is a float constant */
	  && (i = fltpow2(p->Pfloat))	/* and is a power of 2 */
	  && optobj) {
		p->Pvalue = (opr == P_FDVR) ? -i : i;
		p->Pop = opr = P_FSC;
		typ = PTA_RCONST;
		break;
	}

    /*
    ** Optimize moves of constants to use immediate form if possible.
    ** This includes a check for "float" constants, which can be
    ** MOVSI'd into the LH if their RH is zero.  "double" constants
    ** normally do not come here as they use DMOVx, but it is possible
    ** as part of a code sequence that zeros the second AC separately.
    */
    case P_MOVN:
	if (typ != PTV_IMMED || (p->Pvalue &~ 0777777) == 0) break;
	opr = P_MOVE;			/* re-invert P_MOVN */
	p->Pvalue = - p->Pvalue;	/* for fixup into P_MOVSI */
    case P_MOVE:
	if (typ == PTV_IMMED && p->Pvalue != 0 && (p->Pvalue & 0777777) == 0) {
	    opr = P_MOVS;		/* MOVEI of left half quantity */
	    p->Pvalue = ((unsigned) p->Pvalue) >> 18;	/* becomes MOVSI */
	} else
	    /* Check for possibility of setting up a "float" quickly */
	    if (typ == PTA_FCONST && ! bigfloat(p))
		opr = P_MOVS;
	break;
    }
    /* End of moby switch on instruction opcode! */

    /*
    ** Normal instruction, output the opcode.
    ** If it is a MOVEI of an address, we convert it to XMOVEI
    ** so it will work in non-zero sections.
    */

    outop ((typ == PTV_IINDEXED? P_XMOVEI : opr), p->Ptype & PTF_SKIPPED);

    /*
    ** Now output the register and address parts of the instruction.
    ** This can also change the opcode if we wanted the Both or Immediate
    ** form of an instruction.
    */
    p->Ptype = typ;	/* Put back type in case it was changed. */
    outrmem(p, opr);	/* Output register and address, permit opcode hacks */
}
static void
outrmem(p, opr)
PCODE *p;
int opr;		/* Non-zero if OK to hack opcode */
{
    int big = 0, i;

    switch (p->Ptype & PTF_ADRMODE) {

    case PTA_ONEREG:
	outreg(p->Preg);
	break;

    case PTA_REGIS:
	outreg(p->Preg);
	oreg(p->Pr2);
	break;

    case PTA_MINDEXED:
	if ((p->Ptype & PTF_IMM) && opr && opr != P_MOVE) {
	    /*
	    ** The only instruction to correctly implement this addressing
	    ** mode in extended sections is XMOVEI.  Others must be simulated
	    ** by an XMOVEI into a scratch register followed by reg-to-reg
	    ** operation.
	    */

	    outreg(R_SCRREG);		/* simulate by XMOVEI into scratch */
	    outaddress(p);		/* of desired address */
	    outnl();
	    outop (opr, 0);		/* followed by real instruction */
	    outreg(p->Preg);		/* into desired register */
	    oreg(R_SCRREG);		/* from scratch */
	} else {
	    outreg(p->Preg);		/* normal op, use given reg */
	    outaddress(p);		/* and address */
	}
	break;

    case PTA_PCONST:
	outreg(p->Preg);
	outc('[');
	outptr(p->Pptr, p->Pbsize, p->Poffset);
	outc(']');
	break;

    case PTA_BYTEPOINT:
	outreg(p->Preg);
	outc('[');
	outnum(p->Pbsize);	/* Output P+S field */
	outstr(",,");
	outaddress(p);		/* Now add right half addr+offset(index) */
	outc(']');
	break;

    case PTA_RCONST:
	if (!opr				/* If op not given */
	  || (popflg[opr&POF_OPCODE]&PF_EIMM)	/* or op takes E as immed op */
	  || !(p->Ptype & PTF_IMM))		/* or optype not marked "imm"*/
	    big = 0;				/* then just output OP R,val */
	else {
	    /* Op is known and is not an E-immediate type, plus it is
	    ** marked as "immediate".  So either we generate an opI form,
	    ** or make the operand into a memory literal as in OP R,[val].
	    */
	    if ((popflg[opr&POF_OPCODE] & PF_OPI)	/* If op can be opI,*/
	      && (p->Pvalue &~ 0777777) == 0) {	/* and operand has zero LH, */
		outc('I');			/* make immediate op! */
		big = 0;			/* and say small operand */
	    } else big = 1;		/* Nope, use memory literal */
	}
	outreg(p->Preg);		/* Output reg and comma */
	if (big) {
	    outc('[');
	    outnum(p->Pvalue);
	    outc(']');
	}
	else outnum(p->Pvalue);
	break;


    case PTA_FCONST:
	big = 1;			/* Assume big */
	switch(opr & POF_OPCODE) {
	    case P_FADR: case P_FSBR:	/* Single-precision operations */
	    case P_FMPR: case P_FDVR:	/* can be optimized sometimes */
	    case P_MOVS:
		big = bigfloat(p);	/* Set flag 0 if small */

	    default:			/* Always big otherwise */
		if (!big) outc('I');
		outreg(p->Preg);
		fprintf(out, (big ? "[%o]" : "(%o)"),
			*(int *)(&p->Pfloat));	/* Pass float as int val */
		break;
	}
	/* Add a readable comment so humans can understand what the floating
	 * constant is.  However, because this is slow, only do it when
	 * we know the assembler output is going to stay around.
	 */
	if(!delete)			/* If keeping asm file around, */
	    fprintf(out,"\t; %.20g",p->Pfloat);	/* add comment for humans */
	break;		

    case PTA_DCONST1:		/* Double-word floating constant */
    case PTA_DCONST2:
	i = ((p->Ptype)&PTF_ADRMODE) == PTA_DCONST1 ? 1 : 2;
	outreg(p->Preg);
	if (tgmachuse.mapdbl) {	/* If target mach fmt is different */
	    outc('[');
	    outmpdbl(&p->Pdouble, i);	/* Output part of mapped double */
	    outc(']');
	}
	else fprintf(out, "[%o]", i == 1 ? p->Pdouble1 : p->Pdouble2);
	if(!delete)			/* If keeping asm file around, */
	    fprintf(out,"\t; %.20g", p->Pdouble); /* add comment for humans */
	break;

    case PTA_DCONST:		/* Double-word floating constant */
	outreg(p->Preg);
	if (p->Pdouble == 0) {		/* Hack: if a double zero, */
	    outstr(crtsnam[CRT_ZERO]);	/* Use universal constant location. */
	    crtref[CRT_ZERO]++;
	} else if (tgmachuse.mapdbl) {	/* If target mach fmt is different */
	    outc('[');
	    outmpdbl(&p->Pdouble, 3);	/* Output mapped double */
	    outc(']');
	}
	else fprintf(out, "[%o\n\t\t %o]", p->Pdouble1, p->Pdouble2);
	if(!delete)			/* If keeping asm file around, */
	    fprintf(out,"\t; %.20g",p->Pdouble); /* add comment for humans */
	break;

    default:
	error(EINT,"unknown adrmode seen in outrmem(): %d",
					(p->Ptype & PTF_ADRMODE));
    }
    outnl();
}
/* SIMFLTR - Output expansion of "Float and Round" instruction for KA-10s
**	FLTR Preg,M
**		Preg = register to leave single-precision float in.
**		M = any memory reference to single-word integer operand
**
**	This simulation is only invoked for machines without the FLTR
** instruction, which basically just means KA-10s, and it uses the
** usual FSC R,233 hack which is not really the best thing (since it
** screws up if the integer is larger than 27 bits)... but until more
** hair is called for, we'll leave it this way.
*/
static void
simfltr(p)
PCODE *p;
{
    switch (p->Ptype) {
    case PTA_REGIS:
	if (p->Preg != p->Pr2) {	/* FLTR R,R  can skip the MOVE */
    case PTA_MINDEXED:
	    outstr("\tMOVE"); outrmem(p, 0);	/* Get integer into R */
	}
	fprintf(out, "\n\tFSC %o,233\n", p->Preg);
	break;

    case PTA_RCONST:
	if (p->Ptype & PTF_IMM) {
	    fprintf(out,"\tMOVE %o,[%o]\n", p->Preg, (float)p->Pvalue);
	    break;
	}
    default:
	error(EINT,"bad operand for FLTR");
    }
}
/* SIMDFIX - Output expansion of P_DFIX double-to-integer conversion "instr"
**	P_DFIX reg,M
**		reg = register pair, integer will be left in 1st reg.
**		M = any memory ref to double-word float operand.
**
** Note that reg+1 is clobbered!
** Expands into:
**	DMOVE	R,M
**	HLRE	16,R		;This mattered when shifts were bit-at-a-time
**	ASH	16,-11		;Get just exponent (9 bits)
**	JUMPGE	16,.+3		;Positive?
**	  DMOVN	R,R		;No, negate, orig sign still in 1B0[A]
**				; For KA-10 format this is DFN R,R+1.
**	  TRC	16,777777	;Watch for diff between twos and ones comp
**	TLZ	R,777000	;Bash exponent and sign ... now positive
**				; For KA-10 format, LSH R+1,10 goes here.
**	ASHC	R,-233(16)	;Make an integer (may overflow)
**	CAIGE	16,		;Original negative?  Check its sign.
**	 MOVN	R,R		;Yup, negate result.
*/
static void
simdfix(p)
PCODE *p;
{
    /* Check for DFIX R,R and skip the DMOVE if that's what we have */
    if ((p->Ptype&PTF_ADRMODE) != PTA_REGIS || p->Preg != p->Pr2) {
	outstr("\tDMOVE"); outrmem(p, 0);	/* Get double into reg pair */
    }
    fprintf(out, "\tHLRE 16,%o\n\tASH 16,-11\n\tJUMPGE 16,.+3\n", p->Preg);

    if (tgmachuse.dfl_s)		/* Use right kind of double negation */
	fprintf(out, "\tDFN %o,%o\n", p->Preg, p->Preg+1);
    else fprintf(out, "\tDMOVN %o,%o\n", p->Preg, p->Preg);
    outstr("\tTRC 16,-1\n");

    fprintf(out, "\tTLZ %o,777000\n", p->Preg);
    if (tgmachuse.dfl_s)		/* Account for exponent in 2nd wd */
	fprintf(out, "\tLSH %o,10\n", p->Preg+1);
    fprintf(out, "\tASHC %o,-233(16)\n", p->Preg);
    outstr("\tCAIGE 16,\n");		/* Check sign bit of original # */
    fprintf(out, "\t MOVNS %o\n", p->Preg);	/* Negate result */
}
/* SIMSUBBP - Output expansion of P_SUBBP byte-pointer subtraction "instr".
**	P_SUBBP reg,M		[plus Pbsize set to bytesize if known]
**		reg = register pair, 1st reg contains minuend BP
**		M = any memory ref to subtrahend BP
**		Pbsize = bytesize of pointers (> 0 if known)
**			This is currently only used if M operand is P_CONST.
**	Leaves resulting # in 2nd reg!
**
**	This makes use of 2 special symbols from CPU.C and
** some tables in CRT.C, and expands into:
**	Known size		Unknown size
**				LDB 16,[$$BPSZ,,R]	; get PS from R
**	SUB R,M			SUB R,M
**	MULI R,$$BMPn		MUL R,$BPMUL(16)
**	ASH R+1,$$BSHF		ASH R+1,$$BSHF
**				ADD R,$BPADT(16)
**	ADD R+1,$BPADn(A)	ADD R+1,(A)
*/
static void
simsubbp(p)
PCODE *p;
{
    int siz, typ, tbidx;

    if ((typ = (p->Ptype&PTF_ADRMODE)) == PTA_PCONST)
	    siz = p->Pbsize;		/* Aha, size is known! */
    else siz = 0;

    switch (siz) {
	case 6:	tbidx = 0; break;
	case 7:	tbidx = 1; break;
	case 8: tbidx = 2; break;
	case 9: tbidx = 3; break;
	case 18: tbidx = 4; break;
	default:
	    error(EINT, "bad Pbsize for P_SUBBP: %d", siz);
	    siz = 0;
	case 0:
	    fprintf(out, "\tLDB 16,[%s,,%o]\n", crtsnam[CRT_BPSZ], p->Preg);
	    crtref[CRT_BPSZ]++;
    }

    /* Simple check to verify addressing mode is OK */
    switch (typ) {
	case PTA_PCONST:
	case PTA_REGIS:
	case PTA_MINDEXED:
	    break;
	default:
	    error(EINT,"unknown adrmode for SUBBP: %d", typ);
    }
    outstr("\tSUB"); outrmem(p, 0);	/* Output SUB R,M */

    if (siz) {
	fprintf(out, "\tMULI %o,%s\n", p->Preg, crtsnam[CRT_BMP6+tbidx]);
	crtref[CRT_BMP6+tbidx]++;
    } else {
	fprintf(out, "\tMUL %o,%s(16)\n", p->Preg, crtsnam[CRT_BPMUL]);
	crtref[CRT_BPMUL]++;
    }
    fprintf(out, "\tASH %o,-%s\n", p->Preg+1, crtsnam[CRT_BSHF]);
    crtref[CRT_BSHF]++;
    if (!siz) {
	fprintf(out, "\tADD %o,%s(16)\n", p->Preg, crtsnam[CRT_BPADT]);
	crtref[CRT_BPADT]++;
	fprintf(out, "\tADD %o,(%o)\n", p->Preg+1, p->Preg);
    } else {
	fprintf(out, "\tADD %o,%s(%o)\n",
		p->Preg+1, crtsnam[CRT_BPAD6+tbidx], p->Preg);
	crtref[CRT_BPAD6+tbidx]++;
    }
}

/*
** NOTE:
** Byte pointer comparison used to be simulated too but now is done
** with explicit instructions by CCGEN2.
*/
/* NOTE:
** Some PDP-10 architecture machines have no ADJBP instruction.
** We used to expand a sequence for P_ADJBP similar to that for
** P_SUBBP, but now we always just output P_ADJBP
** as ADJBP R,X and it is up to the C-HDR preamble file to redefine
** ADJBP as a macro (in a form similar to the above) if the machine
** does not support ADJBP.  This allows machine-language library routines
** to use ADJBP in their code.
*/
/* SIMPTRCNV - Output expansion of P_PTRCNV pointer conversion "instruction".
**	P_PTRCNV reg,offset	[plus Pbsize set to desired bytesize]
**		reg = register containing pointer to convert
**		offset = bytesize of old pointer
**		Pbsize = desired bytesize of new pointer
**
**	Currently this only supports 9<->18 bit conversions, and the
** code generator shouldn't give us anything else.
** The interaction between the special instruction symbols defined in the
** library $$$CPU module and the code here has been carefully arranged.
** In particular the instructions must take up the same amount of space
** whether being loaded for zero-section or multi-section operation, and
** they must NOT change the value of a NULL (zero) pointer.
**
** The conversions to and from word pointers are done by the code
** generator (with TDZ/TLZ or SKIPE+IOR/TLO) because it may be possible
** for the peephole optimizer to do something with those instructions.
*/
static void
simptrcnv(p)
PCODE *p;
{
    int *ip;
    static int cvH_9[] = {CRT_PH90, CRT_PH91, 0};
    static int cv9_H[] = {CRT_P9H0, CRT_P9H1, CRT_P9H2, 0};

    /* Switch depending on original size plus desired size */
    switch (TGSIZ_WORD* p->Poffset + p->Pbsize) {
	case TGSIZ_WORD* 9 + 18:	/* 9-bit to 18-bit */
	    if (!tgmachuse.exadr) {	/* Coding for local-section only? */
		fprintf(out, "\tTLZE %o,117700\n", p->Preg);
		fprintf(out, "\t TLO %o,002200\n", p->Preg);
		return;
	    }
	    fprintf(out,"\tJUMPE %o,.+4\n", p->Preg);	/* Skip if ptr NULL */
	    ip = cv9_H;
	    break;

	case TGSIZ_WORD* 18 + 9:	/* 18-bit to 9-bit */
	    if (!tgmachuse.exadr) {	/* Coding for local-section only? */
		fprintf(out, "\tTLZE %o,007700\n", p->Preg);
		fprintf(out, "\t TLO %o,111100\n", p->Preg);
		return;
	    }
	    ip = cvH_9;
	    break;
	default:
	    error(EINT, "bad bsize for ptrcnv: %d", p->Pbsize);
	    return;
    }

    /* Wrap up with special instructions the value of which depends
    ** on whether the code is being loaded for a zero-section or
    ** multi-section program.  The specified register is added into
    ** the symbols in such a way that it fits into the AC field.
    */
    for (; *ip; ++ip) {
	fprintf(out,"\t%s+<%o>\n", crtsnam[*ip], p->Preg<<23);
	++crtref[*ip];
    }
}
/* SIMSMOVE - Output expansion of P_SMOVE structure copy "instruction".
**	P_SMOVE reg,addr+offset(idx)  [plus Pbsize set to # words]
**		reg = register containing destination word address
**		addr+offset(idx) = source address
**		Pbsize = # words to copy
*/
static void
simsmove(p)
PCODE *p;
{
    int i, size;

    if ((p->Ptype&PTF_ADRMODE) != PTA_MINDEXED) {
	error(EINT,"bad adrmode for P_SMOVE: %o", p->Ptype);
	return;
    }
    switch (size = p->Pbsize) {
    case 1: case 2: case 3:
	fprintf(out,"\tXMOVEI 16,-1(%o)\n", p->Preg);	/* Get dest <addr-1> */
	while (--size >= 0) {
	    outstr("\tPUSH 16,");
	    outaddress(p);
	    outnl();
	    ++(p->Poffset);
	}
	return;

    default:
	if (size <= 0) {
	    error(EINT,"generated P_SMOVE with bad size %d", size);
	    return;
	}

	/* Code to do local-section BLT */
	if (tgmachuse.exadr)	/* If mach has ext addressing */
	    outstr("\tJUMPGE 17,.+5\n");	/* Must check at runtime. */
	fprintf(out,"\tMOVEI 16,(%o)\n\tHRLI 16,",p->Preg);
	outaddress(p);		/* for HRLI 16,<source-addr> */
	fprintf(out,"\n\tBLT 16,%o(%o)\n", size-1, p->Preg);
		
	/* Code to do global-section XBLT */
	if (!tgmachuse.exadr)	/* If mach doesn't have ext addressing */
	    return;		/* then that's all! */

	/* There is so much setup overhead that we check the size and
	** use PUSHes if there are less than 10 words, which seems to
	** be the breakeven point (paper calculation).
	*/
	if (size < 10) {
	    fprintf(out,"\tJRST .+%o\n", size+2);
	    fprintf(out,"\tXMOVEI 16,-1(%o)\n", p->Preg); /* dest <addr-1> */
	    while (--size >= 0) {
		outstr("\tPUSH 16,");
		outaddress(p);
		outnl();
		++(p->Poffset);
	    }
	    return;
	}
	/* Too big for cleverness, just spit out the XBLT code */
	fprintf(out,"\tJRST .+11\n\tPUSH 17,14\n\tPUSH 17,15\n");
	if (p->Pindex == R_SP)	/* Must adjust stack reference? */
	    p->Poffset -= 2;	/* Yeah, account for our 2 PUSHes */
	if (size <= 0777777)
	    fprintf(out,"\tMOVEI 14,%o\n", size);
	else fprintf(out,"\tMOVE 14,[%o]\n", size);
	outstr("\tXMOVEI 15,"); outaddress(p);
	fprintf(out,"\n\tMOVE 16,%o\n\tEXTEND 14,[XBLT]\n", p->Preg);
	outstr("\tPOP 17,15\n\tPOP 17,14\n");
    }
}
/* SIMUIDIV - Output expansion of P_UIDIV unsigned division "instruction".
**	P_UIDIV reg,<addr>
**		reg = register containing dividend (two-word register)
**		addr = divisor
** This sequence was derived from one suggested by Peter Samson
** at Systems Concepts.
*/
static void
simuidiv(p)
PCODE *p;
{
    register int rq = p->Preg;		/* RQ - Quotient register */
    register int rr = rq+1;		/* RR - Remainder register, RQ+1 */
    register int divisor;

    /* First, try to use optimized sequences if the divisor is a constant. */
    if ((p->Ptype&PTF_ADRMODE) == PTA_RCONST) {
	divisor = p->Pvalue;
	if (divisor == 1 || divisor == 0) {	/* Div by 1 (or 0??) */
	    fprintf(out,"\tSETZ %o,\n", rr);	/* just clears rem */
	    return;
	}
	if ((divisor & (divisor-1)) == 0) {	/* If divisor is power of 2 */
	    fprintf(out,"\tLSHC %o,-%o\n", rq, binexp(divisor));
	    fprintf(out,"\tLSH %o,-%o\n",rr, (TGSIZ_WORD - binexp(divisor)));
	    return;
	}
	if (divisor > 0) {		/* High bit not set? */
	    fprintf(out, "\tSKIPL %o,%o\n", rr, rq);
	    fprintf(out, "\t TDZA %o,%o\n", rq, rq);
	    fprintf(out, "\t  MOVEI %o,1\n", rq);
	    if (!(p->Pvalue & ~0777777))	/* Constant fits in RH? */
		fprintf(out, "\tDIVI %o,%o\n", rq, divisor);
	    else fprintf(out, "\tDIV %o,[%o]\n", rq, divisor);
	    return;
	}
	/* Constant divisor has high bit set, ugh!
	** This case is pretty unlikely so don't bother optimizing it.
	** Just drop through to general-purpose division algorithm.
	*/
	p->Ptype |= PTF_IMM;		/* Ensure outrmem checks bigness */
    }

    /* Sigh, cannot avoid doing full-fledged hairy unsigned division.
    ** Output entire algorithm -- see following comments for more explanation.
    ** Note that divisor is fetched into reg 16 immediately to avoid any
    ** addressing conflicts with regs RQ or RQ+1.  We temporarily set
    ** Preg to this scratch reg so that we can take advantge of outrmem()
    ** and have it use the right register.
    */
    p->Preg = R_SCRREG;				/*        Fake out outrmem() */
    outstr(     "\tSKIPGE"); outrmem(p,P_SKIP);	/* SKIPGE 16,MEM Get divisor */
    p->Preg = rq;				/*		Undo fakeout */
    outstr(     "\t JRST .+10\n");		/*  JRST $1 if divisor neg */
    fprintf(out,"\tJUMPGE %o,.+17\n", rq);	/* JUMPGE RQ,$3 Both +? Win! */


    /* Divisor is positive, but dividend isn't.
    ** Must check for special case of 1, which leaves high-order (sign) bit
    ** still set!  (All other values zero it).  Might as well include 0 here.
    */
    fprintf(out,"\tCAIG %o,1\n", R_SCRREG);	/* CAIG 16,1 Check divisor */
    outstr(     "\t JRST .+14\n");		/*  JRST $2 if 0 or 1 */

    /* Dividend is neg (has high bit set), divisor doesn't.
    ** We know that divisor is at least 2 so the quotient will always
    ** lose at least 1 high bit and thus we can win by doing a DIV without
    ** any fixup.  The DIV is needed rather than IDIV because we have to
    ** divide a 2-word value; the high bit becomes the low bit of the
    ** high-order word.
    */
    fprintf(out,"\tMOVE %o,%o\n", rr, rq);	/* MOVE RR,RQ Set up */
    fprintf(out,"\tMOVEI %o,1\n", rq);		/* MOVEI RQ,1 Get 1 ? dvdend */
    fprintf(out,"\tDIV %o,%o\n", rq, R_SCRREG);	/* DIV RQ,16    Do the div! */
    outstr(     "\tJRST .+12\n");		/* JRST $4	Done! */

/* Label $1: Divisor is negative (high bit is set) */
    /* Because divisor's high bit is set, there's no way the dividend
    ** can be more than twice the magnitude of the divisor.  So the
    ** quotient must be either 0 or 1, with the remainder being respectively
    ** either the dividend or the dividend less 1 times the divisor.
    */
    fprintf(out,"\tMOVE %o,%o\n", rr, rq);	/* MOVE RR,RQ	Make dblwd */
    fprintf(out,"\tMOVEI %o,0\n", rq);		/* MOVEI RQ,0 with high wd 0 */
    fprintf(out,"\tJUMPGE %o,.+7\n", rr);	/* JUMPGE RR,$4 Maybe done */
    fprintf(out,"\tCAMGE %o,%o\n",rr,R_SCRREG);	/* CAMGE RR,16 */
    fprintf(out,"\t JRST .+5\n");		/*  JRST $4 */
    fprintf(out,"\tSUB %o,%o\n", rr, R_SCRREG); /* SUB RR,16 */
    fprintf(out,"\tAOJA %o,.+3\n", rq);		/* AOJA RQ,$4 */

/* Label $2: Divisor is 0 or 1, dividend is neg */
    fprintf(out,"\tTDZA %o,%o\n", rr, rr);	/* TDZA RR,RR  Clear rem */
						/*	and skip next instr */
/* Label $3: Divisor and dividend both positive */
    fprintf(out,"\t IDIV %o,%o\n",rq,R_SCRREG);	/* IDIV RQ,16 */
/* Label $4: Done! */

}
/*
** Emit opcode and skip fields
**
** This also writes out the tab before the opcode, but not the one after.
** We give a skipped-over op an extra space, to make it look more like
** human code (big deal) and to make debugging KCC easier.
*/
static void
outop(opr, skipped)
{
    outtab();
    if (skipped) outc (' ');		/* indent skipped-over op */
    if (opr == 0) error(EINT, "null machine op");
    outstr (popostr [opr & POF_OPCODE]);

    switch (opr & POF_OPSKIP) {
    case POS_SKPA: outc ('A'); break;
    case POS_SKPE: outc ('E'); break;
    case POS_SKPN: outc ('N'); break;
    case POS_SKPL: outc ('L'); break;
    case POS_SKPG: outc ('G'); break;
    case POS_SKPLE: outstr ("LE"); break;
    case POS_SKPGE: outstr ("GE"); break;
    }

    if (opr & POF_BOTH) switch (opr) {
    case P_MOVN+POF_BOTH: case P_MOVM+POF_BOTH: outc ('S'); break;
    default: outc ('B'); break;
    }
}
/* OUTMPDBL - Output mapped double-format constant
**	This is also used by CCGEN for data.
*/
void
outmpdbl(ip, which)
int *ip;
int which;		/* 1 = 1st wd, 2 = 2nd wd, 3 = both wds */
{
    int second;		/* 2nd word is the different one */
    int exp;		/* Gotta derive new exponent */

    if (second = ip[1])	/* Only if low order word is non-zero */
	switch (tgmachuse.mapdbl) {
    case 1:	/* Internal format is hardware, output in software format */
	exp = (*ip < 0 ? -*ip : *ip) & ~((1<<27)-1);	/* Mask off pos exp */
	second = (((unsigned)second) >> 8) | (exp - (27<<27));
	break;
    case -1:	/* Internal format is software, output in hardware format */
	second = (second << 8) & ~(1<<35);	/* Just flush 2nd exp! */
	break;
    default:
	error(EINT,"bad double map");
    }
    if (which&01) outpos(*ip);		/* 1st wd always output as is */
    if (which==03) outstr("\n\t\t");
    if (which&02) outpos(second);	/* 2nd word has been mapped */
}

/* BINEXP - count zero bits to right of rightmost 1 in word.
**	Used for converting a power-of-2 value into a shift count.
**	Also called by CCFOLD for constant folding.
*/
int
binexp(n)
unsigned int n;
{
    int e;

    e = -1;				/* init count of bits to shift */
    do {
	n >>= 1;			/* logical shift over one */
	e++;				/* and count a zero */
    } while (n != 0);			/* until that was the last bit */
    return e;				/* return number of bits */
}
/* --------------------------------------- */
/*      subroutines for opcode output      */
/* --------------------------------------- */
static void
outreg(n)
{
    outc (' ');				/* separate with space */
    if (n > 0) {
	oreg(n);
	outc(',');
    }
}

static void
oreg(n)
{
    outnum (realreg (n));
}

static void
outaddress(p)
PCODE *p;
{
    if (p->Ptype & PTF_IND) outc('@');	/* if indirect, say so with atsign */

    if (p->Pptr != NULL) {		/* now right half: */
	outid(p->Pptr->Sname);		/* symbol */
	if (p->Poffset) {		/* with offset */
	    /* Do trick here of multiplying by 1.  This is only needed for
	    ** FAIL to force it into making Polish, to avoid wrong-seg
	    ** relocation bug (see FAIL manual doc for the TWOSEG pseudo).
	    ** MACRO and MIDAS aren't bothered by it.
	    */
	    if (tgasm == TGASM_FAIL)
		outstr("*1");		/* Hack hack */
	    if (p->Poffset > 0) outc ('+');
	    outnum(p->Poffset);
	}
    } else outnum(p->Poffset);		/* no sym, just give offset */

    if (p->Pindex) {			/* now output index register */
	if (p->Pindex == R_SP		/* Simple check - if stack index */
	  && p->Poffset > 0		/* ensure it points to valid stuff */
	  && (p->Ptype&PTF_IMM)==0)	/* or if not, that we only want addr */
					/* Else making bad stack reference! */
	    error(EINT,"pcode instr with positive stack offset");
	outc ('(');
	outnum (p->Pindex);
	outc (')');
    }
}
/* ------------------------------------------------------ */
/* fltpow2(d) - See if arg is positive and a power of 2   */
/* ------------------------------------------------------ */
/*	Returns zero if not, else non-zero integer exponent.
 * Very machine-dependent, only works for standard single-precision
 * PDP-10 floating point format, and assumes a normalized number.
 *	If the number is a power of 2, only bit 9 will be set in
 * the mantissa (the rest will be 0) and the exponent field (bits 1-8)
 * can tell us what we want to know - it is in excess 128 code, so
 * that an exponent X for the fractional mantissa is represented by X+128.  
 * Subtracting 129 then gives us the power of 2 for the integer.
 *	Minor screw: 1.0 (2 to the 0th) returns 0 which is the same as
 * an error return.  However, the two cases of using 0.0 or 1.0 as operand
 * should have been caught earlier in optimization.
 */
static int
fltpow2(d)
double d;
{
	if(d > 0.0 && ((*(int *)(&d)) & 03777777777) == 0)
	    return( ((unsigned)(*(int *)(&d)) >> 27) - 129);
	return(0);
}

/* ------------------------------------------------------ */
/*  check if big floating point (cannot all fit into LH)  */
/*  Note this only checks floats, not doubles!            */
/* ------------------------------------------------------ */
static int
bigfloat(p)
PCODE *p;
{
    return(((*(int *)(&p->Pfloat))
		 & 0777777) != 0); 	/* If 1st wd RH has bits, big. */
}
/*
**      assembler preamble and postamble
*/

static void
outpreamble()
{
    if (tgasm == TGASM_MIDAS)		/* This MIDAS pseudo */
	outstr(".SYMTAB 8111.,8003.\n");	/* must come before TITLE. */
    outstr("\tTITLE\t");		/* Make TITLE pseudo-op */
    outstr(module);			/* with name of input file as title */
    outnl();

    /* Now output the Assembler Header, either by generating it
    ** ourself or copying it from a user-specified file.
    */
    outasmh();
    whichseg = 1;		/* Now starting in code section */
}

void
postamble(mainf)
int mainf;			/* TRUE if main() was defined in file */
{
    register int i, c;
    register SYMBOL *s;

    outnl();
    codeseg();				/* make sure in code segment */

    /* Specify machine types usable with this module */
    for (c = TGCPU_N; --c >= 0;)
	if (tgcpu[c] == 0) {	/* Impossible CPUs have their symbol set 0 */
	    switch(c) {
		case TGCPU_KA:	i = CRT_CPUKA;	break;
		case TGCPU_KI:	i = CRT_CPUKI;	break;
		case TGCPU_KS:	i = CRT_CPUKS;	break;
		case TGCPU_KL0:	i = CRT_CPUKL0;	break;
		case TGCPU_KLX:	i = CRT_CPUKLX;	break;
		default: error(EINT,"bad CPU type");
			i = CRT_CPUKL0;
	    }
	    outstr(crtsnam[i]);
	    outstr("==0\n\t");
	    outstr(tgasm == TGASM_MIDAS ? ".GLOBAL " : "INTERN ");
	    outstr(crtsnam[i]);
	    outnl();
	}

    if (tgasm == TGASM_MIDAS) {		/* output literals and finish */
	dataseg();
	outstr("\tVARIABLES\n");	/* Put any accumulated vars here */
	codeseg();			/* Then back to code seg */
	outstr("\n\tCONSTANTS\n");	/* for accumulated literals */
    } else outstr("\n\tLIT\n");

    /* "main" module needs entry vector set.  This crock is necessary
    ** because FAIL requires that the arg to END be defined in the current
    ** module!
    */
    if (mainf) {
	outstr("$$STRT: JRST $START\n");
	outstr("\tJRST $START+1\n");
	outstr("\t0\n");			/* 3rd wd for T20 ver info */
	++crtref[CRT_START];
    }

    /* Output EXTERN declarations for any C runtime symbols we used */

    /* Normally the CPU and CRT modules are always reffed, but just
    ** to make life easier for us KCC implementors, we check here
    ** for compiling those modules themselves, and avoid sending out
    ** an EXTERN if so!
    */
    if ((s = findsym("`$$$CRT"))
	&& (s->Sclass==SC_EXTERN || s->Sclass==SC_AEXTERN)
	&& s->Svalue)		/* Is defined */
	    crtref[CRT_CRT] = 0;	/* Pretend not reffed */
    if ((s = findsym("`$$$CPU"))
	&& (s->Sclass==SC_EXTERN || s->Sclass==SC_AEXTERN)
	&& s->Svalue)		/* Is defined */
	    crtref[CRT_CPU] = 0;	/* Pretend not reffed */

    /* Emit symbol requests for all referenced C runtime support stuff */
    for (i = CRT_N; --i >= 0; )
	if (crtref[i]) {
	    outstr(tgasm == TGASM_MIDAS ? "\t.GLOBAL " : "\tEXTERN\t");
	    outstr(crtsnam[i]);
	    outnl();
	}
#if 0
    if (bytsym.Svalue) outstr("\tEXTERN\t$BYTE\n");
#endif

    outdecl(0);			/* Output user-defined INTERN and EXTERNs */

    /* Finally, output entry vector if any */
    if (mainf)
	outstr("\tEND <3,,$$STRT>\n");	/* 3 wds in entry vector */
    else
	outstr("\tEND\n");
}
/*
**	Output assembler external & internal declarations
*/

static int
outdecl(flag)
int flag;		/* 0 = definitions are INTERNed, 1 = ENTRYed */
{
    SYMBOL *s;
    int num = 0;

    for (s = symbol->Snext; s != NULL; s = s->Snext)
	if (s->Sclass == SC_EXTERN || s->Sclass == SC_AEXTERN) {
	    if(s->Svalue) {	/* Symbol defined in this module */
		num++;
		if (tgasm == TGASM_MIDAS)
		    outstr(".GLOBAL\t");
		else outstr(flag ? "\tENTRY\t" : "\tINTERN\t");
	    }
	    else {		/* Symbol not defined.  Ask for it if needed */
		if (s->Srefs == 0)		/* If never used, don't ask. */
			outc(';');		/* Not used, comment out. */
		outstr(tgasm == TGASM_MIDAS ? "\t.GLOBAL\t" : "\tEXTERN\t");
	    }
	    outid (s->Sname);
	    outnl();
	}
    return num;
}


/* OUTLAB - Output label
**	Note: the ==. syntax is used for local labels because that is
** acceptable to all of FAIL, MACRO, and MIDAS.  The "proper" half-killed label
** syntax for MACRO is "foo:!", but it is "foo::" for the others.
** We cleverly avoid the issue with ==.
*/
void
outlab(s)
SYMBOL *s;
{
    outid(s->Sname);		/* Output the actual label name */
    if (s->Sname[0] == '$')	/* Local label? */
	outstr("==.\n");	/* Yes, define it as half-killed.  See note. */
    else outstr(":\n");		/* No, normal label. */
}
/* MAKPREFILE - determines whether a prefix assembler file is needed
**	and creates it if so.  Returns non-zero if one was created.
**	Always generates one for MIDAS since an .INSRT was already put
**	in the output MID file!
*/
int
makprefile(fname)
char *fname;
{
    SYMBOL *sym;
    FILE *savout, *fpre;
    int nexfs = 0;

    /* Scan symbol table to see if there are any names
    ** we should export as library entry points.
    */
    if (tgasm == TGASM_MIDAS) nexfs++;	/* Fake out */
    else {
	for(sym = symbol->Snext; sym != NULL; sym = sym->Snext)
	    if(sym->Sclass == SC_EXTERN	/* An external symbol */
	      || sym->Sclass == SC_AEXTERN)
		if (sym->Svalue)	/* which is defined in this module */
		    break;
	if(sym == NULL) return 0;	/* Maybe found none */
    }

    if ((fpre = fopen(prefname, "w")) == NULL) {
	jmsg(EFILOPN, "prefix output", prefname);
	return 0;
    }
    savout = out;
    out = fpre;		/* Set this so can use outstr and outid */

    /* OK, now output entry statements for each exportable symbol */
    for(; sym != NULL; sym = sym->Snext)
	if((sym->Sclass == SC_EXTERN || sym->Sclass == SC_AEXTERN)
					/* An external symbol */
	    && sym->Svalue) {		/* which is defined in this module */
		if (tgasm == TGASM_MIDAS)
		    outstr(".GLOBAL ");
		else outstr("ENTRY ");
		outid(sym->Sname);
		outnl();
		++nexfs;
	}
    out = savout;	/* Restore old pointer if any */

    fclose(fpre);
    return(nexfs);
}
/* ------------------------------------------------ */
/*      swap to other of high and low segments      */
/* ------------------------------------------------ */
void
codeseg()
{
    if (whichseg == 0) outpreamble();	/* make sure preamble set up */
    if (whichseg < 0) {			/* if in data */
	outstr("\n\t%%CODE\n");		/* put in code instead */
	whichseg = 1;			/* and remember in code */
    }
}

void
dataseg()
{
    if (whichseg == 0) outpreamble();	/* make sure preamble set up */
    if (whichseg > 0) {			/* if in code */
	outstr("\n\t%%DATA\n");		/* put in data instead */
	whichseg = -1;			/* and remember in data */
    }
}
/* Assembler file output functions */

/* ----------------------------------------- */
/*      output string to assembler file      */
/* ----------------------------------------- */
void
outstr(s)
char *s;
{
    if (*s) {
	do putc(*s, out);
	while (*++s);
    }
}

/* ----------------------------------------- */
/*      output symbol to assembler file      */
/* ----------------------------------------- */
void
outid(s)
char *s;
{
    register int n, ch;

    n = 6;				/* Max # chars to output */
    if ((ch = *s) == SPC_IDQUOT)	/* Handle quoted identifier syms */
	ch = *++s;
    while (ch) {
	if (ch == '_') ch = '.';
	putc(ch, out);
	if (--n <= 0) break;
	ch = *++s;
    }
}

/* -------------------------------------------- */
/*      output character to assembler file      */
/* -------------------------------------------- */
void
outc(n)
{
    putc(n, out);
}
void
outtab()
{
    putc('\t', out);
}
void
outnl()
{
    putc('\n', out);
}

/* ----------------------------- */
/*      output octal number      */
/* ----------------------------- */
void
outnum(n)
{
    int k;

    if (n < 0) {
	n = -n;
	putc('-', out);
    }
    if (n &~ 07) outpos((unsigned) n >> 3);
    putc((n & 07) + '0', out);
}

static void
outpos(n)
{
    int k;

    if (n &~ 07) outpos((unsigned) n >> 3);
    putc((n & 07) + '0', out);
}
/* OUTPTI(p) - output instruction with immediate pointer operand.
**	This is used for TLZ and TLO.
**	We already know that the instruction's operand is a PTA_PCONST
** with no symbol or offset, so the RH is zero and only the LH is needed.
** Note $$SECT should not be used as we are masking BP position bits rather
** than address bits.  Error if byte offset is so large that a word offset
** becomes necessary.
*/
static void
outpti(p)
PCODE *p;
{
    int i, woff = 0;
    outop(p->Pop, p->Ptype&PTF_SKIPPED);	/* Output opcode */
    outreg(p->Preg);				/* and AC register */
    if ((i = obplh(p->Poffset, &woff, p->Pbsize)) == 0
      || woff) {
	error(EINT,"bad bsize or offset in outpti()");
	i = CRT_BPPS;
    }
    outstr(crtsnam[i]);		/* Output byte pointer bits */
    ++crtref[i];
    outnl();
}

/* OUTPTR - output pointer value, for word or byte pointer.
**	sym - address identifier (if non-NULL)
**	offset - offset in bytes from given address
**	bsize - size of bytes in bits.
**		0 = word pointer, no P+S in left half.
*/
void
outptr(sym, bsize, offset)
SYMBOL *sym;		/* Identifier to point at */
int bsize;		/* Bytesize of pointer (0 = word) */
int offset;		/* # units offset from identifier */
{
    register int i;

    if (bsize && (i = obplh(offset, &offset, bsize))) {
	    outstr(crtsnam[i]);		/* Output byte pointer bits */
	    ++crtref[i];
	    outstr("+");
    }

    /* Now do rest of pointer (the word address) */
    outstr(crtsnam[CRT_SECT]);	/* Make global word address */
    ++crtref[CRT_SECT];
    outstr(",,");
    if (sym) {
	if (sym->Sclass == SC_ISTATIC)
	    outid(sym->Ssym->Sname);	/* Internal static symbol */
	else outid(sym->Sname);		/* Normal external symbol */
    } else outstr("0");
    if (offset) {		/* If non-zero word offset, */
	outstr("*1");		/* See outaddress() for crock explanation */
	if (offset > 0) outc('+');	/* Add or subtract it */
	outnum(offset);		/* note outnum prefixes '-' if negative */
    }	
}

/* OBPLH - Find BP left-half value
**	If bsize = -1, use P+S mask.  This is meaningless for most all
** ops except P_TDZ or P_TLZ, however.
*/
static int
obplh(boff, awoff, bsize)
int boff;	/* Byte offset */
int *awoff;	/* Addr of place to return word-offset to */
int bsize;	/* Byte size (0 if word) */
{
    switch (bsize) {
	default:
	    error(EINT,"bad bytesize in outptr");
	case -1:		/* Return P+S mask */
	    return CRT_BPPS;
	case 0:			/* Word pointer */
	    return 0;
	case 6:
	    return CRT_BP60 + adjboffset(boff, awoff, 6);
	case 7:
	    return CRT_BP70 + adjboffset(boff, awoff, 5);
	case 8:
	    return CRT_BP80 + adjboffset(boff, awoff, 4);
	case 9:
	    return CRT_BP90 + adjboffset(boff, awoff, 4);
	case 18:
	    return CRT_BPH0 + adjboffset(boff, awoff, 2);
    }
}


/* ADJBOFFSET - Adjust byte offset.  Auxiliary for obplh() and foldbyte().
*/
int
adjboffset(boff, awoff, bpw)
int boff;			/* Byte offset to adjust */
int *awoff;			/* Place to deposit word offset */
int bpw;			/* # bytes per word */
{
    if (boff < 0) {
	/* Negative increment, need to go back far enough
	** that boff can become a positive offset within a word.
	*/
	*awoff = -((-boff) / bpw);	/* Find -# words */
	if (boff = (-boff) % bpw) {	/* If any bytes, */
	    (*awoff)--;			/* must bump back 1 more word */
	    return bpw - boff;		/* and return positive offset. */
	} else return 0;
    } else {		/* Positive increment, simple. */
	*awoff = boff / bpw;		/* Find # words */
	return boff %= bpw;		/* and remaining # bytes */
    }
}
/* OUTSCON - Output string constant.
**	Used to always produce an ASCIZ string, but now uses
** whatever bytesize is specified.
** The major hack to be noted here is that 6-bit characters are
** converted to PDP-10 SIXBIT!
*/
void
outscon(s, l, bsiz)
char *s;		/* Char string */
int l;			/* Length (may include nulls!) */
int bsiz;		/* Byte size to use. */
{
    int i, sepchar = ',';
    char *opstr = "BYTE\t(%d) ";

    if (tgasm == TGASM_MIDAS)
	sepchar = '?', opstr = ".BYTE %d ? ";

    --s;			/* Set up for preincrement */
    while (l > 0) {			/* For each word */
	fprintf(out, opstr, bsiz);	/* Start bytes of given size */
	for (i = (TGSIZ_WORD/bsiz);;) {
	    if (bsiz == 6) outnum(*++s ? (toupper(*s)-040) : 0);
	    else outnum(*++s);		/* output char value */
	    if (--l <= 0) break;
	    if (--i <= 0) break;
	    outc(sepchar);
	}
	outstr("\n\t");
    }
    if (tgasm == TGASM_MIDAS) outstr(" ? .BYTE");
    outnl();
}
/*
** KCC Assembler Header output
**
**	Copyright (c) 1986 by Ken Harrenstien, SRI International
**
** This section contains the code required to generate the assembler header
** text that KCC puts at the beginning of all its assembler
** output files, just after any ENTRY or TITLE statements, and before
** any generated code.
**
** The header for both FAIL and MACRO is identical.
** However, MIDAS requires a somewhat different syntax.
*/

/* Imported functions */
extern char *malloc();
extern char *estrcpy();		/* CCASMB */

/* Internal functions and data */
static void ahmacs();
static char *ahmacdef(), *ahdfls();
#if 0
static char segfai[], segmid[];
typedef char *amac_t[];		/* amac_t specifies an array of char ptrs */
static amac_t macdmv, macdmm, macjsp,
	macdb1, macdb2, macjbp,
	macdsv, macdrs, macdnv, macdnm,
	macdfad, macdfsb, macdfmp, macdfdv;
#endif
static char * macdmv[] = {	"DMOVE",
	"MOVE A,M",
	"MOVE A+1,1+M",
	NULL
};
static char * macdmm[] = {	"DMOVEM",
	"MOVEM A,M",
	"MOVEM A+1,1+M",
	NULL
};

static char * macjsp[] = {	"ADJSP",
	"ADD A,[<M,,M>+<M&<-1,,0>>]",
	NULL
};

/*
** Byte pointer operations.
**
** ADJBP AC,MEM
**    takes a byte count in AC and a byte pointer of any shape in MEM,
**    and leaves a byte pointer shifted over by the count in the AC.
**
** %CHRBP AC,MEM
**    backs up the byte pointer in MEM by one and leaves it in AC.
**    This is often used in the runtimes to produce an ILDB pointer
**    from an IBP pointer.  The result may not quite be the same as
**    corresponding AC -- it may be a (POINT n,X) pointer i.e. have
**    a P field of 44 -- so only do this once and don't index through
**    the result or compare it to another non-%CHRBP pointer.
*/

static char * macdb1[] = { 	"%CHRBP",	/* For machines with ADJBP */
	"SETO A,",
	"ADJBP A,M",
	NULL
};
static char * macjbp[] = {	"ADJBP",
	"EXTERN	$ADJBP",	/* Declare extern routine to simulate it */
	"MOVE	16,M",		/* Do this before EXCH.  Get byte pointer */
	"EXCH	15,A",		/* Put count into expected reg, save AC15 */
	"PUSHJ	17,$ADJBP",	/* Call $ADJBP to simulate byte ptr add */
	"EXCH	15,A",		/* restore AC15 and get adjusted bp in reg */
	NULL
};

static char * macdb2[] = {	"%CHRBP",
	"MOVE	A,M",		/* Get byte pointer */
	"ADD	A,[110000,,0]",	/* Adjust assuming 9-bit bytes */
	"TLNN	A,007000",	/* Was it 7-bit byte pointer? */
	" SUB	A,[020000,,0]",	/* Yes, fix up for that case */
	NULL,
};
/*
** KA-10 Double-Precision Floating Point (software format)
**
** Copied from:
**    DEC Assembly Language Handbook, 1st edition, 1972
**    PDP-10 System Reference Manual  DEC-10-HGAC-D
**	3rd printing August 1969
**	Page 2-67, "Programming Examples".
**
** NOTE!  These macros require 3 registers, not just 2!
** We can save things in AC16 and be sure that A+2 is free
** (KCC reserves AC16 as a temporary for just such purposes).
** We never use doublewords beyond 14/15 so A+2 always exists.
**
** Some of these sequences are less than efficient, but they
** have to be sufficiently general to cover all possible abuses
** (e.g. unexpected registers used as indices in addresses).
*/


	/* %DSAVE A,M - Set up A+2/free, M into -2(17)/-1(17): */
static char * macdsv[] = {	"%DSAVE",
	"MOVE	16,1+M",	/* Get low word (push here would mess up M) */
	"PUSH	17,M",		/* Now safe to push high word */
	"PUSH	17,16",		/* Followed by low word (careful if A+2==16) */
	"PUSH	17,A+2",	/* Followed by AC to be freed */
	NULL
};
	/* %DREST A, - Restore at end of double op after %DSAVE: */
static char * macdrs[] = {	"%DREST",
	"POP	17,A+2",	/* Get saved register back */
	"ADJSP	17,-2",		/* Forget about pushed copy of doubleword */
	NULL
};
	/* DMOVN A,M - Get double from memory and negate: */
static char * macdnv[] = {	"DMOVN",
	"MOVE	A,M",		/* Get high part of double */
	"MOVE A+1,1+M",		/* Then low part */
	"DFN	A,A+1",		/* Do double register negate */
	NULL
};
	/* DMOVNM A,M - Save negated doubleword into memory: */
static char * macdnm[] = {	"DMOVNM",
	"DFN	A,A+1",		/* Negate register doubleword */
	"MOVEM	A,M",		/* Store high part of negated word into mem */
	"MOVEM	A+1,1+M",	/* And low part */
	"DFN	A,A+1",		/* Negate reg again to leave the way it was */
	NULL
};
	/* DFAD A,M - Add two doubles: */
static char * macdfad[] = {	"DFAD",
	"%DSAVE	A,M",		/* Set up for doubleword op */
	"UFA	A+1,-1(17)",	/* Sum of low parts to A+2 */
	"FADL	A,-2(17)",	/* Sum of high parts to A,A+1 */
	"UFA	A+1,A+2",	/* Add low part of high sum to A+2 */
	"FADL	A,A+2",		/* Add low sum to high sum */
	"%DREST A,0",		/* Fix up after doubleword op */
	NULL
};
	/* DFSB A,M - Subtract double in mem from one in acs: */
static char * macdfsb[] = {	"DFSB",
	"DFN	A,A+1",		/* Negate double length operand */
	"DFAD	A,M",		/* Perform normal add */
	"DFN	A,A+1",		/* -(M - AC) = AC - M */
	NULL
};
	/* DFMP A,M - Multiply two doubles: */
static char * macdfmp[] = {	"DFMP",
	"%DSAVE	A,M",		/* Set up for doubleword op */
	"MOVEM	A,A+2",		/* Copy high AC operand in A+2 */
	"FMPR	A+2,-1(17)",	/* High reg times low mem to A+2 */
	"FMPR	A+1,-2(17)",	/* Low reg times high mem to A+1 */
	"UFA	A+1,A+2",	/* Add cross products into A+2 */
	"FMPL	A,-2(17)",	/* High product to A,A+1 */
	"UFA	A+1,A+2",	/* Add low part to cross sum in A+2 */
	"FADL	A,A+2",		/* Add low sum to high part of product */
	"%DREST A,0",		/* Fix up after doubleword op */
	NULL
};
	/* DFDV A,M - Divide double in acs by one in mem: */
static char * macdfdv[] = {	"DFDV",
	"%DSAVE	A,M",		/* Set up for complicated double op */
	"FDVL	A,-2(17)",	/* Get high part of quotient */
	"MOVN	A+2,A",		/* Copy negative of quotient in A+2 */
	"FMPR	A+2,-1(17)",	/* Multiply by low part of divisor */
	"UFA	A+1,A+2",	/* Add remainder */
	"FDVR	A+2,-2(17)",	/* Divide sum by high part of divisor */
	"FADL	A,A+2",		/* Add result to original quotient */
	"%DREST A,0",		/* Fix up after doubleword op */
	NULL
};
/*
** Data segmentation.
**
** KCC emits code in two sections: code and data.  This file sets things up
** so that code and data are in the high and low segments, which typically
** correspond to the high and low halves of their section but which can be
** rearranged by the appropriate LINK directives.
**
** Code and data are separated in the assembly output by the %%CODE and %%DATA
** macros.  %%CODE will never be called from any context other than the data
** segment, and vice versa.  KCC expects the header to initially be in code.
*/

static char segfai[] = "\
	TWOSEG	400000	\n\
	RELOC	0	\n\
	RELOC	400000	\n\
	DEFINE %%CODE <RELOC>\n\
	DEFINE %%DATA <RELOC>\n";

/* For MIDAS the code/data macros use .M" to ensure that the definitions they
** make are valid even if used within a .BEGIN symbol block.
*/
static char segmid[] = "\
	.DECTWO 400000	\n\
	$$RLO==.	\n\
	.=.+400000	\n\
	$$RHI==.	\n\
DEFINE %%CODE		\n\
.M\"$$RLO==. ? .=.M\"$$RHI	\n\
TERMIN			\n\
DEFINE %%DATA		\n\
.M\"$$RHI==. ? .=.M\"$$RLO	\n\
TERMIN\n";
/* OUTASMH -  Output assembler header file.
**
** Copy Assembler Header File from its default or user-specified location.
** This should end up in the code segment, and supply macros
** %%CODE and %%DATA for switching between the two.
*/
static char *asmhdr = NULL;
static void
outasmh()
{
    FILE *hdrf;
    register int c;

    /* If user explicitly specified an assembler header file, use it. */
    if (asmhfile) {
	if ((hdrf = fopen(asmhfile, "r")) == NULL) /* open file */
	    fatal (EFILOPN, "preamble", asmhfile); /* no luck, give up */
	while ((c = getc (hdrf)) != EOF) outc (c); /* copy file */
	if (ferror(hdrf) || !feof(hdrf))
	    error(EINT, "I/O error while reading preamble file");
	fclose(hdrf);
    } else {
	/* Otherwise, use self-generated header. */
	if (!asmhdr) ahmacs();	/* Make it if it doesn't already exist */
	outstr(asmhdr);		/* Output it! */
    }
}

static void
ahmacs()
{
    register char *cp;
    int size = 2000;		/* Hack for now */

    if ((asmhdr = malloc(size)) == NULL)
	fatal(EINT, "cannot allocate space for assembler header");
    cp = asmhdr;

    /* Make request for standard C library (unless using MIDAS).
    ** Eventually flush this request and rely on KCC linker invocation.
    */
    if (tgasm != TGASM_MIDAS)
	cp = estrcpy(estrcpy(estrcpy(estrcpy(estrcpy(cp,
		"\t.REQUEST "), libprefix), "c"), libpostfix), "\n");
    else
	/* If MIDAS, take the opportunity to .INSRT the prefix file here. */
	cp = estrcpy(estrcpy(estrcpy(cp, ".INSRT "), prefname), "\n");

    /* Specify version of compiler and library used with this module */
    sprintf(cp,"\t$$CVER==<%o,,%o>\n\t%s $$CVER\n",
		cvercode, cverlib,
		(tgasm == TGASM_MIDAS) ? ".GLOBAL" : "INTERN");		
    cp += strlen(cp);			/* Sigh, update ptr */

    /* DMOVx */
    if (!tgmachuse.dmovx) {
	cp = ahmacdef(cp, macdmv);	/* DMOVE */
	cp = ahmacdef(cp, macdmm);	/* DMOVEM */
    }

    /* ADJSP */
    if (!tgmachuse.adjsp)
	cp = ahmacdef(cp, macjsp);	/* ADJSP */

    /* ADJBP and %CHRBP */
    if (tgmachuse.adjbp) {
	/* FAIL and MACRO need to have ADJBP defined! */
	if (tgasm == TGASM_FAIL || tgasm == TGASM_MACRO)
	    cp = estrcpy(cp, "\tOPDEF ADJBP [IBP]\n");
	cp = ahmacdef(cp, macdb1);		/* %CHRBP */
    } else {
	cp = ahmacdef(cp, macjbp);		/* ADJBP */
	cp = ahmacdef(cp, macdb2);		/* %CHRBP */
    }

    /* KA-10 Software double-precision instructions */
    if (tgmachuse.dfl_s)
	cp = ahdfls(cp);

    /* Miscellaneous op-code definitions.
    ** ERJMP and ERJMPA are for TOPS-20 and TENEX systems only, for
    **	the benefit of library (or user) #asm code.
    ** ERJMPA should be used instead of ERJMP when the JSYS normally skips.
    **
    ** XMOVEI and IFIW are used with extended addressing code.
    ** XBLT likewise; this is actually an EXTEND sub-operation.
    */
    if (tgasm == TGASM_FAIL || tgasm == TGASM_MACRO) {
	if (tgsys == TGSYS_TOPS20 || tgsys == TGSYS_TENEX)
	    cp = estrcpy(cp, "IFNDEF ERJMP,< OPDEF ERJMP [JUMP 16,] >\n");
	if (tgsys == TGSYS_TOPS20)
	    cp = estrcpy(cp,  "OPDEF ERJMPA [ERJMP]\n");
	else cp = estrcpy(cp, "OPDEF ERJMPA [JUMPA 16,]\n");
	cp = estrcpy(cp, "OPDEF	XMOVEI	[SETMI]\n\tDEFINE IFIW <SETZ >\n");
    } else if (tgasm == TGASM_MIDAS) {
	cp = estrcpy(cp, (tgsys == TGSYS_TOPS20
		? "ERJMPA==<ERJMP>\n"		/* T20 */
		: "ERJMPA==<JUMPA 16,>\n"));	/* otherwise */
	cp = estrcpy(cp, "IFIW==<SETZ>\n");	
    }
    if (tgasm == TGASM_FAIL)
	cp = estrcpy(cp, "OPDEF XBLT [020000,,0]\n");
    else if (tgasm == TGASM_MIDAS)
	cp = estrcpy(cp, "XBLT==<020000,,0>\n");

    
    /* Code and data segmentation setup and %%DATA/%%CODE macro definition */
    if (tgasm == TGASM_FAIL || tgasm == TGASM_MACRO)
	cp = estrcpy(cp, segfai);
    else if (tgasm == TGASM_MIDAS) cp = estrcpy(cp, segmid);


/*
** Finally, we try to purge symbols which may conflict with user-defined syms.
** However, there is a bug in FAIL which causes SEARCH MONSYM to barf
** ("FAIL BUG IN SEARCH") if the SEARCH comes after the IF's are purged!
** But if we put the SEARCH here, before the PURGE, we are including it in ALL
** KCC output, which is not a good idea; there is more overhead, and
** otherwise undefined symbols might be unexpectedly satisfied from MONSYM!
** For the time being, since FAIL will output a .REL file anyway even after
** barfing about the SEARCH, we will continue doing this, and include a
** note in the doc to ignore those barfs, and hope someone fixes FAIL someday.
**
** Note that MACRO will not complain, and consequently MACRO is preferred
** when compiling modules with #asm code (such as the C library!)
*/
    if (tgasm == TGASM_FAIL || tgasm == TGASM_MACRO)
	cp = estrcpy(cp, "\
PURGE IFE,IFN,IFG,IFGE,IFL,IFLE,IFDEF,IFNDEF,IFIDN,IFDIF\n");
    else if (tgasm == TGASM_MIDAS)
	cp = estrcpy(cp, "\
IF1 [EQUALS $IFE,IFE?EQUALS $IFN,IFN?EQUALS $IFG,IFG?EQUALS $IFL,IFL\n\
  EQUALS $IFGE,IFGE?EQUALS $IFLE,IFLE?EQUALS $IFSE,IFSE?EQUALS $IFSN,IFSN\n\
  EQUALS $IFDEF,IFDEF?EQUALS $IFNDEF,IFNDEF?EQUALS $IRP,IRP\n\
  EXPUNGE IFE,IFN,IFG,IFGE,IFL,IFLE,IFSE,IFSN,IFDEF,IFNDEF,IRP\n\
]\n");

}

static char *
ahmacdef(cp, macro)
char *cp;
char **macro;
{
    switch (tgasm) {
	case TGASM_FAIL:
	case TGASM_MACRO:
	    cp = estrcpy(estrcpy(estrcpy(cp,"DEFINE "),macro[0]),"(A,M)\n<");
	    while (*++macro)
		cp = estrcpy(estrcpy(estrcpy(cp, "\t"), *macro), "\n");
	    cp = estrcpy(cp, ">\n");
	    break;
	case TGASM_MIDAS:
	    cp = estrcpy(estrcpy(estrcpy(cp,"DEFINE "),macro[0])," A,M\n");
	    while (*++macro)
		cp = estrcpy(estrcpy(estrcpy(cp, "\t"), *macro), "\n");
	    cp = estrcpy(cp, "TERMIN\n");
	    break;
    }
    return cp;
}


static char *
ahdfls(cp)
char *cp;
{
    cp = ahmacdef(cp, macdsv);	/* %DSAVE */
    cp = ahmacdef(cp, macdrs);	/* %DREST */
    cp = ahmacdef(cp, macdnv);	/* DMOVN */
    cp = ahmacdef(cp, macdnm);	/* DMOVNM */
    cp = ahmacdef(cp, macdfad);	/* DFAD */
    cp = ahmacdef(cp, macdfsb);	/* DFSB */
    cp = ahmacdef(cp, macdfmp);	/* DFMP */
    cp = ahmacdef(cp, macdfdv);	/* DFDV */
    return cp;
}