📄 ccout.c
字号:
** 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 + -