⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ccout.c

📁 KCC , a good c compiler, write by Ken Harrenstien
💻 C
📖 第 1 页 / 共 5 页
字号:
** This sequence was derived from one suggested by Peter Samson
** at Systems Concepts.
*/
static void
simuidiv (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\t%o,\n", rr);	/* just clears rem */
	    return;
	    }
	if ((divisor & (divisor-1)) == 0)	/* If divisor is power of 2 */
	    {
	    fprintf (out,"\tLSHC\t%o,-%lo\n", rq, binexp (divisor));
	    fprintf (out,"\tLSH\t%o,-%lo\n",rr, (TGSIZ_WORD - binexp (divisor)));
	    return;
	    }
	if (divisor > 0)		/* High bit not set? */
	    {
	    fprintf (out, "\tSKIPL\t%o,%o\n", rr, rq);
	    fprintf (out, "\t TDZA\t%o,%o\n", rq, rq);
	    fprintf (out, "\t  MOVEI\t%o,1\n", rq);
	    if (! (p->Pvalue & ~0777777L))	/* Constant fits in RH? */
		fprintf (out, "\tDIVI\t%o,%lo\n", rq, (INT) divisor);
	    else
		fprintf (out, "\tDIV\t%o,[%lo]\n", rq, (INT) 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 outinstr 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 advantage of outinstr ()
    ** and have it use the right register.
    */
    p->Pop = P_SKIP+POF_ISSKIP+POS_SKPGE;	/* Modify instr to fake out */
    p->Preg = R_SCRREG;				/*       call to outinstr () */
    outinstr (p);				/* SKIPGE 16,MEM Get divisor */
    outstr (     "\t JRST\t.+10\n");		/*  JRST $1 if divisor neg */
    fprintf (out,"\tJUMPGE\t%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\t%o,1\n", R_SCRREG);	/* CAIG 16,1 Check divisor */
    outstr (     "\t JRST\t.+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\t%o,%o\n", rr, rq);	/* MOVE RR,RQ Set up */
    fprintf (out,"\tMOVEI\t%o,1\n", rq); /* MOVEI RQ,1 Get 1 ? dvdend */
    fprintf (out,"\tDIV\t%o,%o\n", rq, R_SCRREG); /* DIV RQ,16  Do the div! */
    outstr (     "\tJRST\t.+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\t%o,%o\n", rr, rq);	/* MOVE RR,RQ	Make dblwd */
    fprintf (out,"\tMOVEI\t%o,0\n", rq); /* MOVEI RQ,0 with high wd 0 */
    fprintf (out,"\tJUMPGE\t%o,.+7\n", rr);	/* JUMPGE RR,$4 Maybe done */
    fprintf (out,"\tCAMGE\t%o,%o\n",rr,R_SCRREG);	/* CAMGE RR,16 */
    fprintf (out,"\t JRST\t.+5\n");		/*  JRST $4 */
    fprintf (out,"\tSUB\t%o,%o\n", rr, R_SCRREG); /* SUB RR,16 */
    fprintf (out,"\tAOJA\t%o,.+3\n", rq);		/* AOJA RQ,$4 */

/* Label $2: Divisor is 0 or 1, dividend is neg */
    fprintf (out,"\tTDZA\t%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\t%o,%o\n",rq,R_SCRREG);	/* IDIV RQ,16 */
/* Label $4: Done! */
}


/* OUTOP - Emit opcode and condition-test fields
**
*/
static void
outop (int opr)
{
    if (opr == 0)
	int_error ("outop: null op");

    outstr (popostr[opr & POF_OPCODE]);	/* Output assembler opcode mnemonic */

    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;
	default:
	    ;	/* do nothing */
	}

    if (opr & POF_BOTH)
	switch (opr)
	    {
	    case P_MOVN+POF_BOTH:
	    case P_MOVM+POF_BOTH:
		outc ('S');
		break;
	    default:
		outc ('B');
		break;
	    }
}

/* OUTREG - Output register field
*/
static void
outreg (int n)
{
    outtab ();

    if (n > 0)
	{
	if (mlist && Register_Preserve (n))
	    outid (Reg_Id[n - (r_maxnopreserve + 1)]->Sname); /* FW 2A (47) */
	else if (mlist && n == R_SP)
	    outid ("SP");
	else
	    outnum (n);
	outc (',');
	}
}

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

    if (p->Pptr != NULL)		/* now right half: */
	{
	outmiref (p->Pptr);		/* 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 (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->Poffset > 01000000L)	/* ensure valid 18 bit address */
	    int_error ("outaddress: bad stk offset 0%o", p->Poffset);
	outc (' (');

	if (mlist && Register_Preserve (p->Pindex))
	    outid (Reg_Id[p->Pindex -
		   (r_maxnopreserve + 1)]->Sname); /* FW 2A (47) */
	else if (mlist && p->Pindex == R_SP)
	    outid ("SP");
	else
	    outnum (p->Pindex);
	outc (')');
	}
}

/* OUTPTI - output 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, 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.
** Bytesize of pointer (0 for illegal word ptr) and # bytes offset from 
** start of word.
*/
static void
outpti (int bsize, INT offset)
{
    int i;
    INT woff = 0;

    if ((i = obplh (offset, &woff, bsize)) == 0
      || woff)
	{
	int_error ("outpti: bad args");
	i = CRT_BPPS;
	}
    outstr (crtsnam[i]);		/* Output byte pointer bits */
    ++crtref[i];
}

/* 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.
** If the address identifier is NULL, no $$SECT is output for the LH.
** This could be used to create absolute pointers.
** Identifier to point at and Bytesize of pointer (0 = word) and  # units 
** offset from identifier
*/

void
outptr (SYMBOL *sym, int bsize, INT offset)
    {
    register int i = 0;


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

    /* Now do rest of pointer (the word address) */

#ifdef	MULTI_SECTION /* FW 2A (51) */
    if (sym)
	{
	if (i)
	    outstr ("+");		/* Combine with P+S if any */
	outstr (crtsnam[CRT_SECT]);	/* Make global word address */
	++crtref[CRT_SECT];
	}
#endif
 
    outstr (",,");

    if (sym)
	outmiref (sym);		/* Either internal static, or normal extern */
    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 (INT boff, INT *awoff, int bsize)
/* Byte offset, Addr of place to return word-offset to, Byte size (0 if word)*/
{
    switch (bsize)
	{
	default:
	    int_error ("obplh: bad bsize: %d", bsize);
	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 foldadjbp ().
*/
int
adjboffset (INT boff, INT *awoff, int bpw)
/* Byte offset to adjust, Place to deposit word offset, # 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) != 0)	/* If any bytes, */
	    {
	    (*awoff)--;			/* must bump back 1 more word */
	    return bpw - (int) boff;	/* and return positive offset. */
	    }
	else
	    return 0;
	}
    else		/* Positive increment, simple. */
	{
	*awoff = boff / bpw;		/* Find # words */
	return (int) (boff % bpw);		/* and remaining # bytes */
	}
}

/* Floating-point auxiliary functions */

/* OUTFLT - Output floating-point constant value.
**	Handles float, double, long double.
**	If no flags given, assumes outputting all of value as a static data
**	constant (used by CCGEN), else a code literal constant.
**	Returns the number of words emitted.
*/
#define OF_CONST 0100	/* Code literal constant */
#define OF_WD1 01	/* Emit word 1 only */
#define OF_WD2 02	/* Emit word 2 only */

int
outflt (int typ, INT *ptr, int flags)
/* Tspec of value, Generic pointer to object, and flags */
{
    if (!flags)
	flags = OF_WD1|OF_WD2;
    if (flags & OF_CONST)
	outc ('[');		/* Set up as literal constant ] */
    else
	outtab ();
    if (typ == TS_FLOAT)
	{
	outpnum (*ptr);
	typ = 1;
	}
    else if (tgmachuse.mapdbl)	/* If target mach fmt is different */
	{
	outmpdbl (ptr, 3);		/* output mapped double */
	typ = 2;
	}
    else
	{
	outpnum (ptr[0]);
	outnl ();
	outtab ();
	if (flags & OF_CONST)
	    outtab ();
	outpnum (ptr[1]);
	typ = 2;
	}
    if (flags & OF_CONST)
	outc (']');

    /* 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, add comment */
	fprintf (out, "\t; %.20g", * (double *)ptr);
    if (! (flags & OF_CONST))
	outnl ();
    return typ;		/* Return # wds emitted */
}

static void		/* output exactly n digits octal unsigned int */
outlpnum (unsigned INT n, int dig)
{
    if (dig > 1)
	outlpnum (n >> 3, dig - 1);
    putc ((n & 7) + '0', out);
}

/* OUTMPDBL - Output mapped double-format constant
**	This is also used by CCGEN for data.
*/
static void
outmpdbl (INT *ip, int which)  /* 1 = 1st wd, 2 = 2nd wd, 3 = both wds (dbl) */
{
#ifdef  __COMPILER_KCC__		/* native floating point format */

    INT second;		/* 2nd word is the different one */
    INT exp;		/* Gotta derive new exponent */

    if ((second = ip[1]) != 0)	/* 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 INT)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:
		int_error ("outmpdbl: bad map");
	    }
    if (which&01)
	outpnum (*ip);		/* 1st wd always output as is */
    if (which==03)
	outstr ("\n\t\t");
    if (which&02)
	outpnum (second);	/* 2nd word has been mapped */
#else			/* not native - assume IEEE as used by Turbo C */
    /*
     *	MSDOS version:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -