Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - c/old/kcc/ccout.c
There are 8 other files named ccout.c in the archive. Click here to see a list.
/* <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 (struct 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"
/* ------------------------ */
/*      generate label      */
/* ------------------------ */

glabel(s)
char *s;
{
    flushcode();			/* clear out previous code */
    outsym (s);				/* put the actual label name */
    outc (':');				/* say it's a label */
    if (s[0] == '$') {			/* local label? */
	outc (asmtyp==AST_MACRO ? '!' : ':');	/* yes, tell the assembler */
    }
    nl();
}
/* ---------------------------- */
/*      generate real code      */
/* ---------------------------- */

realcode(n)
{
    pcode p, q, a;
    char *op, *t;
    int opr, typ, i, big, simop, simreg;

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

    big = 0;
    typ = (p->ptype &~ SKIPPED);
    simop = 0;

    switch ((opr = p->pop) & OPCODE) {

    case NOP:
	return;

    /*
    ** Some PDP-10 architecture machines have no ADJBP instruction.
    ** Thus we have a -b switch available to prevent ADJBPs from being
    ** compiled.  If the code generation wanted to make one, we have
    ** to simulate it.
    **
    ** Similarly, we always have to simulate byte pointer subtraction,
    ** and coercion of a byte pointer to something that can be compared.
    */

    case SUBBP:
    case BPCNT:

	/*
	** ADJBP/SUBBP/BPCNT R,X become
	**	MOVE	16,X
	**	EXCH	R,15
	**	PUSHJ	17,$ADJBP/$SUBBP/$BPCNT
	**	EXCH	R,15
	*/
	simop = opr;
	opr = p->pop = MOVE;
	simreg = p->preg;
	p->preg = SCRREG;
	bytsim++;			/* remember we called one */
	break;

    case DFIX:
	fltsim++;
	if (p->preg == SCRREG - 1) {

	    /*
	    ** DFIX 15,X becomes
	    ** 	    DMOVE   15,X
	    ** 	    PUSHJ   17,$DFIX
	    */

	    outstr("\tDMOVE 15,");
	    outaddress(p);
	    outstr("\n\tPUSHJ 17,$DFIX\n");
	    return;
	}
	if ((typ & ADRMODE) == MINDEXED && p->preg == p->pindex) {

	    /*
	    ** DFIX R,X(R) becomes
	    **	    XMOVEI  16,X(R)
	    **	    MOVE    R,15
	    **	    DMOVE   15,(16)
	    **	    PUSHJ   17,$DFIX
	    **	    EXCH    R,15
	    */

	    outstr("\tXMOVEI 16,");
	    outaddress(p);
	    outstr("\tMOVE");
	    outreg (p->preg);
	    outstr("15\n\tDMOVE 15,(16)\n\tPUSHJ 17,$DFIX\n\tEXCH");
	    outreg (p->preg);
	    outstr("15\n");
	    return;
	}
	if (typ == REGIS && p->pr1 == p->pr2) {

	    /*
	    ** DFIX R,R becomes
	    **	    MOVEM R+1,16
	    **	    EXCH R,15
	    **	    PUSHJ 17,$DFIX
	    **	    EXCH R,15
	    */

	    outstr("\n\tMOVEM");
	    outreg (p->preg + 1);
	    outstr("16\n\tEXCH");
	    outreg (p->preg);
	    outstr("15\n\tPUSHJ 17,$DFIX\n\tEXCH");
	    outreg (p->preg);
	    outstr("15\n");
	    return;
	}
	if (typ == REGIS && p->pr1 == p->pr2 + 1) {

	    /*
	    ** DFIX R,R-1 becomes
	    **	    MOVEM R,16
	    **	    MOVE R,15
	    **	    MOVEM R-1,15
	    **	    PUSHJ 17,$DFIX
	    **	    EXCH 15,R
	    */

	    outstr ("\tMOVEM");
	    outreg (p->preg);
	    outstr ("16\n\tMOVE");
	    outreg (p->preg);
	    outstr ("15\n\tMOVEM");
	    outreg (p->preg - 1);
	    outstr ("15\n\tPUSHJ 17,$DFIX\n\tEXCH");
	    outreg (p->preg);
	    outstr ("15\n");
	    return;
	}

	/*
	** DFIX R,X becomes
	**	MOVE R,15
	**	DMOVE 15,X
	**	PUSHJ 17,$DFIX
	**	EXCH 15,R
	*/

	simop = opr;
	opr = p->pop = DMOVEM;
	simreg = p->preg;
	p->preg = SCRREG - 1;
	outstr ("\tMOVE");
	outreg (simreg);
	outstr ("15\n");
	break;

    case DFLOT:

	/*
	** 	DFLOT R,
	** becomes 
	**	DFAD R,$ZERO
	** where $ZERO is defined in C-HDR.FAI as a doubleword zero.
	*/

	outstr ("\tDFAD");
	outreg (p->preg);
	outstr ("$ZERO\n");
	return;

    case SPUSH:
    case SPOP:

	/*
	** SPUSH/SPOP R,X become
	**	XMOVEI 16,X
	**	EXCH 15,R
	**	PUSHJ 17,$SPUSH/$SPOP
	**	EXCH 15,R
	*/

	typ |= IMM;
	simop = opr;
	simreg = p->preg;
	p->pop = opr = MOVE;
	p->preg = SCRREG;
	break;

    /*
    ** 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 CAIE etc to 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 TRN:
	if (foldtrna (p)) return;
    case CAI:	case TRO:   case TRC:	case TRZ:
	if (typ == MINDEXED) {
	    outstr("\tXMOVEI");		/* turn immediate compare of addr */
	    outreg(SCRREG);		/* int XMOVEI+compare for extended */
	    outaddress(p);		/* addressing. */
	    nl();
	    typ = REGIS;		/* CAIN R,addr becomes */
	    p->pr2 = SCRREG;		/* CAMN R,16 */
	    opr = directop(opr);
	    break;
	}

	if (typ != RCONST || (p->pvalue &~ 0777777) == 0) break;
	if ((p->pvalue & 0777777) == 0) switch (opr & OPCODE) {
	case TRN:
	    opr ^= TRN ^ TLN;
	    p->pvalue >>= 18;
	    break;
	case TRO:
	    opr ^= TRO ^ TLO;
	    p->pvalue >>= 18;
	    break;
	case TRZ:
	    opr ^= TRZ ^ TLZ;
	    p->pvalue >>= 18;
	    break;
	case TRC:
	    opr ^= TRC ^ TLC;
	    p->pvalue >>= 18;
	    break;
	}
	if (!(p->pvalue &~ 0777777)) break; /* still too big? */
	opr = directop(opr);		/* turn into memory version */
	big = 1;			/* force literal for RCONST */
	break;

    /*
    ** Non-test bitwise operations
    ** We try turning these into tests to save a memory reference
    ** from a literal constant.
    */

    case IOR:
	if (typ != IMMED || (p->pvalue &~ 0777777) == 0) break;
	if ((p->pvalue & 0777777) == 0) { /* x,,0 */
	    opr = p->pop = TLO;
	    typ = RCONST;
	    p->pvalue >>= 18;
	} else if ((p->pvalue >> 18) == 0777777) { /* 777777,,x */
	    opr = p->pop = ORCM;
	    p->pvalue = (~p->pvalue) & 0777777;
	}
	break;

    case XOR:
	if (typ != IMMED || (p->pvalue &~ 0777777) == 0) break;
	if ((p->pvalue & 0777777) == 0) { /* x,,0 */
	    opr = p->pop = TLC;
	    typ = RCONST;
	    p->pvalue >>= 18;
	} else if ((p->pvalue >> 18) == 0777777) { /* 777777,,x */
	    opr = p->pop = EQV;
	    p->pvalue = (~p->pvalue) & 0777777;
	}
	break;

    case AND:
	if (typ != IMMED || (p->pvalue &~ 0777777) == 0) break;
	if ((p->pvalue & 0777777) == 0777777) { /* x,,777777 */
	    opr = p->pop = TLZ;
	    typ = RCONST;
	    p->pvalue = (~p->pvalue) >> 18;
	} else if ((p->pvalue >> 18) == 0777777) { /* 777777,,x */
	    opr = p->pop = TRZ;
	    typ = RCONST;
	    p->pvalue = (~p->pvalue) & 0777777;
	}
	break;

    /*
    ** Multiplication by a power of two is better done as an ASH
    ** (arithmetic shift - not the same as LSH).
    **
    ** Similarly, floating multiply by two becomes FSC (floating scale).
    ** Otherwise we fall thruogh to the other floating code.
    */

    case IMUL:
	if (typ == IMMED && (p->pvalue & (p->pvalue - 1)) == 0 && optimize) {
	    p->pvalue = binexp(p->pvalue); /* get number of bits shift */
	    p->pop = opr = ASH;		/* and opcode */
	    typ = RCONST;		/* and code type */
	}
	break;

    case FMPR:
    case FDVR:
	if (typ == PFLOAT) {
	    i = 0;			/* keep count of twos */
	    while (p->pmantissa % 5 == 0) {
		p->pmantissa /= 5;	/* drop powers of five */
		i++;			/* count how many we dropped */
	    }
	    if (p->pexponent + i == 0 && optimize &&
		(p->pmantissa & (p->pmantissa - 1)) == 0) {
		p->pvalue = binexp(p->pmantissa) - i;
		if (opr == FDVR) p->pvalue = - p->pvalue;
		p->pop = opr = FSC;
		typ = RCONST;
		break;
	    }
	    while (i-- > 0) p->pmantissa *= 5; /* put powers back */
	}

    /*
    ** Big actually means small here - if set this is a left-half-only
    ** floating point number.  The floating point ops handle this simply
    ** by using the I form of the instruction, but MOVE must be converted
    ** into MOVSI.
    */

    case FADR:
    case FSBR:
	if (typ == PFLOAT) big = smallfloat(p);
	break;

    case MOVN:
	if (typ != IMMED || (p->pvalue &~ 0777777) == 0) break;
	opr = MOVE;			/* re-invert MOVN */
	p->pvalue = - p->pvalue;	/* for fixup into MOVSI */
    case MOVE:
	if (typ == IMMED && p->pvalue != 0 && (p->pvalue & 0777777) == 0) {
	    opr = MOVS;			/* MOVEI of left half quantity */
	    p->pvalue >>= 18;		/* becomes MOVSI */
	} else if (typ == PFLOAT && (big = smallfloat(p))) opr = MOVS;
	break;
    }

    /*
    ** 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 == IINDEXED? XMOVEI : opr), p->ptype & SKIPPED);

    /*
    ** Now output the register and adress 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 if changed */
    switch (typ & ADRMODE) {
    case BYTEPOINT:
	outreg(p->preg);
	outc('[');
	outnum(p->pindex);
	outstr(",,");
	p->pindex = 0;			/* don't try to use as index reg */
	outaddress(p);			/* now add right half addr/offset */
	outc(']');
	break;

    case RCONST:
	if (typ & IMM) switch (opr & OPCODE) {
	default:			/* normally check left half bits */
	    if (!(p->pvalue &~ 0777777)) {
		outc('I');		/* small, make immediate op */
		break;			/* don't set big to 1 */
	    }
	case PUSH:			/* ops with no immediate counterpart */
	case SKIP:			/* are always big */
	case IFIW:
	    big = 1;
	}
	outreg(p->preg);
	if (big) {
	    outc('[');
	    outnum(p->pvalue);
	    outc(']');
	}
	else outnum(p->pvalue);
	break;

    case PFLOAT:
	if (big) outc('I');
	outreg(p->preg);
	fprintf(out, "%c%d.0E%d%c", big?'(':'[', p->pmantissa,
				  p->pexponent, big?')':']');
	break;

    case REGIS:
	outreg(p->pr1);
	oreg(p->pr2);
	break;

    case ONEREG:
	outreg(p->pr1);
	break;

    case MINDEXED:
	if ((typ & IMM) && opr != 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(SCRREG);		/* simulate by XMOVEI into scratch */
	    outaddress(p);		/* of desired address */
	    nl();
	    outop (opr, 0);		/* followed by real instruction */
	    outreg(p->preg);		/* into desired register */
	    oreg(SCRREG);		/* from scratch */
	} else {
	    outreg(p->preg);		/* normal op, use given reg */
	    outaddress(p);		/* and address */
	}
	break;
    }
    nl();

    switch (simop) {
    case ADJBP:
    case SUBBP:
    case BPCNT:
    case SPUSH:
    case SPOP:
	if (simreg != SCRREG - 1) {
	    outstr ("\tEXCH");
	    outreg (simreg);
	    outstr ("15\n");
	}
    case DFIX:
	outstr ("\tPUSHJ 17,$");
	outstr (dec20op[simop]);
	nl();
	if (simreg != SCRREG - 1) {
	    outstr ("\tEXCH");
	    outreg (simreg);
	    outstr ("15\n");
	}
    }
    return;
}
/*
** 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.
*/

outop(opr, skipped)
{
    tab();
    if (skipped) outc (' ');		/* indent skipped-over op */
    outstr (dec20op [opr & OPCODE]);

    switch (opr & OPSKIP) {
    case SKPA: outc ('A'); break;
    case SKPE: outc ('E'); break;
    case SKPN: outc ('N'); break;
    case SKPL: outc ('L'); break;
    case SKPG: outc ('G'); break;
    case SKPLE: outstr ("LE"); break;
    case SKPGE: outstr ("GE"); break;
    }

    if (opr & BOTH) switch (opr) {
    case MOVN+BOTH: case MOVM+BOTH: outc ('S'); break;
    default: outc ('B'); break;
    }
}
/* ----------------------------- */
/*      count powers of two      */
/* ----------------------------- */

binexp(n)
{
    int e;

    e = -1;				/* init count of bits to shift */
    do {
	n >>= 1;			/* 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      */
/* --------------------------------------- */

outreg(n)
{
    outc (' ');				/* separate with space */
    if (n > 0) {
	oreg(n);
	outc(',');
    }
}

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

tab()   { outc ('\t'); }
comma() { outc (',');  }
nl()    { outc ('\n'); }

outaddress(p)
struct PSEUDO *p;
{
    struct SYMBOL s;

    if (p->ptype & IND) outc('@');	/* if indirect, say so with atsign */

    if (p->pptr != NULL) {		/* now right half: */
	outsym (p->pptr->sname);	/* symbol */
	if (p->poffset) {		/* with offset */
	    if(asmtyp==AST_FAIL)	/* Trick FAIL into making Polish, */
		outstr ("*$$ONE");	/* to avoid wrong-seg reloc bug */
					/* (see FAIL manual doc for TWOSEG) */
	    if (p->poffset > 0) outc ('+');
	    outnum (p->poffset);
	}
    } else outnum(p->poffset);		/* no sym, just give offset */

    if (p->pindex) {			/* now output index register */
	outc ('(');
	outnum (p->pindex);
	outc (')');
    }
}
/* --------------------------------------- */
/*      check if small floating point      */
/* --------------------------------------- */

smallfloat(p)
struct PSEUDO *p;
{
    int mantissa, exponent;

    mantissa = p->pmantissa;
    if (mantissa < 0) return 0;
    if (mantissa == 0) return 1;	/* protect against infinite loop */

    exponent = p->pexponent;
    while (exponent < 0 && mantissa % 5 == 0) {
	exponent++;			/* flush powers of five */
	mantissa /= 5;			/* in both exponent and mantissa */
    }
    while (!(mantissa&1)) mantissa >>= 1; /* flush powers of two */

    switch (exponent) {
    case 0:
	return (mantissa <= 0777);
    case 1:
	return (mantissa <= (0777 / 5));
    case 2:
	return (mantissa <= (0777 / 25));
    default:
	return 0;			/* exponent out of range */
    }
}
/* ------------------------------------------ */
/*      external & internal declarations      */
/* ------------------------------------------ */

gdecl()
{
    struct SYMBOL *s;

    for (s = symbol->snext; s != NULL; s = s->snext)
	if (s->sclass == SEXTERN) {
	    outstr (s->svalue? "\tINTERN\t" : "\tEXTERN\t");
	    outsym (s->sname);
	    nl();
	}
}
/* ----------------------------- */
/*      assembler preambles      */
/* ----------------------------- */

static whichseg;			/* 0/no preamble, 1/code, -1/data */

preamble()
{
    whichseg = 0;			/* no seg yet */
}

static outpreamble()
{
    FILE *hdrf;
    char c;

    outstr("\tTITLE\t");		/* make TITLE pseudo-op */
    outstr(module);			/* with name of input file as title */
    nl();

    if (link) {
	outstr ("\n\t.TEXT\t\"");	/* pass along to LINK */
	outstr (module);		/* module name */
	outstr ("/SAVE\"\n");		/* as name for EXE file */
    }
    if (asmtyp==AST_FAIL)
	outstr ("\tEXTERN\t$$ONE\n"); /* declare sym for reloc hack */

    /*
    ** Copy header from C:C-HDR.FAI or user supplied header.
    ** This should end up in the code segment, and supply macros
    ** $$CODE and $$DATA for switching between the two.
    */

    if ((hdrf = fopen(hdrfile, "r")) == NULL) /* open file */
	fatal (EFILOPN, "preamble", hdrfile); /* no luck, give up */
    while ((c = getc (hdrf)) != EOF) outc (c); /* copy file */
    fclose(hdrf);
    whichseg = 1;			/* starting in code section */
}
/* ------------------------------ */
/*      assembler postambles      */
/* ------------------------------ */

postamble()
{
    struct SYMBOL *s, *findsym();

    nl();
    if (asmtyp==AST_FAIL)
	outstr ("\t$$ONE==1\n");	/* now safe to define reloc hack sym */
    if (bytsym.svalue) outstr("\tEXTERN\t$BYTE\n");
    if (bytsim) outstr("\tEXTERN\t$ADJBP,$SUBBP,$BPCNT\n");
    if (fltsim) outstr("\tEXTERN\t$DFIX,$DFLOT\n");
    if (spushes) outstr("\tEXTERN\t$SPUSH,$SPOP\n");

    gdecl();				/* output INTERN and EXTERNs */
    codeseg();				/* make sure in code segment */
    outstr("\n\tLIT\n");		/* output literals and finish */
    if ((s = findsym("main")) && s->svalue) { /* main needs starting addr */
	outstr("$$STRT:\tJRST\t$START\n");
	outstr("\tJRST\t$START\n");
	outstr("\tEND\t<2,,$$STRT>\n");
    } else
	outstr("\tEND\n");
}
/* ------------------------------------------------ */
/*      swap to other of high and low segments      */
/* ------------------------------------------------ */

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

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 */
    }
}
/* ----------------------------------------- */
/*      output string to assembler file      */
/* ----------------------------------------- */

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

outsym(s)
char *s;
{
    int n;

    n = 0;
    while (*s) {
	putc(((*s == '_') ? '.' : *s), out);
	if (++n > 5) break;
	s++;
    }
    return n;
}
/* -------------------------------------------- */
/*      output character to assembler file      */
/* -------------------------------------------- */

outc(n)
{
    putc(n, out);
}
/* ----------------------------- */
/*      output octal number      */
/* ----------------------------- */

outnum(n)
{
    int k;

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

outpos(n)
{
    int k;

    if (n &~ 07) outpos(n>>3);
    putc((n & 07) + '0', out);
}
/* ---------------------- */
/*      asciz string      */
/* ---------------------- */

asciz(s, l)
char *s;
{
    while (1) {
	outstr("BYTE\t(7) ");
	outnum(*s++);
	if (--l <= 0) break;
	outc(',');
	outnum(*s++);
	if (--l <= 0) break;
	outc(',');
	outnum(*s++);
	if (--l <= 0) break;
	outc(',');
	outnum(*s++);
	if (--l <= 0) break;
	outc(',');
	outnum(*s++);
	if (--l <= 0) break;
	outstr("\n\t");
    }
    nl();
}
/* ------------------------ */
/*	flush peephole      */
/* ------------------------ */

flushcode()
{
    while (mincode < maxcode) realcode (mincode++);
    previous = NULL;
}