+/*
+ * Convert a float or double in the accumulator into an unsigned int.
+ * Unlike the vax, the tahoe stores 0 into the destination
+ * on a conversion of > 2 ** 31, so we compensate.
+ */
+float_to_unsigned(p)
+ NODE *p;
+{
+ register NODE *l = p->in.left;
+ int label1 = getlab();
+ int label2 = getlab();
+ int label3 = getlab();
+ NODE *src, *dst;
+
+ if (p->in.op == SCONV) {
+ src = p->in.left;
+ dst = resc;
+ } else {
+ src = p->in.right;
+ dst = p->in.left;
+ }
+
+ printf(".data\n\t.align\t2\nL%d:\n\t.long\t0x50000000", label1);
+ if (src->in.type == DOUBLE)
+ putstr(", 0x00000000 # .double");
+ else
+ putstr(" # .float");
+ putstr(" 2147483648\n\t.text\n\tcmp");
+ prtype(src);
+ printf("\tL%d\n\tjlss\tL%d\n\tsub", label1, label2);
+ prtype(src);
+ printf("\tL%d\n\tcv", label1);
+ prtype(src);
+ putstr("l\t");
+ adrput(dst);
+ putstr("\n\taddl2\t$-2147483648,");
+ adrput(dst);
+ printf("\n\tjbr\tL%d\nL%d:\n\tcv", label3, label2);
+ prtype(src);
+ putstr("l\t");
+ adrput(dst);
+ printf("\nL%d:", label3);
+}
+
+/*
+ * Convert an unsigned int into a float or double, leaving the result
+ * in the accumulator.
+ */
+unsigned_to_float(p)
+ register NODE *p;
+{
+ int label1 = getlab();
+ int label2 = getlab();
+ NODE *src, *dst;
+
+ if (p->in.op == SCONV) {
+ src = p->in.left;
+ dst = resc;
+ } else {
+ src = p->in.right;
+ dst = p->in.left;
+ }
+
+ printf(".data\n\t.align\t2\nL%d:\n\t.long\t0x50800000", label2);
+ if (p->in.type == DOUBLE)
+ putstr(", 0x00000000 # .double");
+ else
+ putstr(" # .float");
+ putstr(" 4294967296\n\t.text\n\tmovl\t");
+ adrput(src);
+ putchar(',');
+ adrput(dst);
+ putstr("\n\tcvl");
+ prtype(p);
+ putchar('\t');
+ adrput(dst);
+ printf("\n\tjgeq\tL%d\n\tadd", label1);
+ prtype(p);
+ printf("\tL%d\nL%d:", label2, label1);
+}
+
+/*
+ * Prlen() is a cheap prtype()...
+ */
+static char convtab[SZINT/SZCHAR + 1] = {
+ '?', 'b', 'w', '?', 'l'
+};
+#define prlen(len) putchar(convtab[len])
+
+
+/*
+ * Generate code for integral scalar conversions.
+ * Some of this code is designed to work around a tahoe misfeature
+ * that causes sign- and zero- extension to be defeated in
+ * certain circumstances.
+ * Basically if the source operand of a CVT or MOVZ instruction is
+ * shorter than the destination, and the source is a register
+ * or an immediate constant, sign- and zero- extension are
+ * ignored and the high bits of the source are copied. (Note
+ * that zero-extension is not a problem for immediate
+ * constants.)
+ */
+sconv(p, forcc)
+ NODE *p;
+ int forcc;
+{
+ register NODE *src, *dst;
+ register NODE *tmp;
+ register int srclen, dstlen;
+ int srctype, dsttype;
+ int val;
+
+ if (p->in.op == ASSIGN) {
+ src = p->in.right;
+ dst = p->in.left;
+ dstlen = tlen(dst);
+ dsttype = dst->in.type;
+ } else if (p->in.op == SCONV) {
+ src = p->in.left;
+ dst = resc;
+ dstlen = tlen(p);
+ dsttype = p->in.type;
+ } else /* if (p->in.op == OPLEAF) */ {
+ src = p;
+ dst = resc;
+ dstlen = SZINT/SZCHAR;
+ dsttype = ISUNSIGNED(src->in.type) ? UNSIGNED : INT;
+ }
+
+ if (src->in.op == REG) {
+ srclen = SZINT/SZCHAR;
+ srctype = ISUNSIGNED(src->in.type) ? UNSIGNED : INT;
+ } else {
+ srclen = tlen(src);
+ srctype = src->in.type;
+ }
+
+ if (src->in.op == ICON) {
+ if (src->tn.lval == 0) {
+ putstr("clr");
+ prtype(dst);
+ putchar('\t');
+ adrput(dst);
+ return;
+ }
+ if (dstlen < srclen) {
+ switch (dsttype) {
+ case CHAR:
+ src->tn.lval = (char) src->tn.lval;
+ break;
+ case UCHAR:
+ src->tn.lval = (unsigned char) src->tn.lval;
+ break;
+ case SHORT:
+ src->tn.lval = (short) src->tn.lval;
+ break;
+ case USHORT:
+ src->tn.lval = (unsigned short) src->tn.lval;
+ break;
+ }
+ }
+ if (dst->in.op == REG) {
+ dsttype = INT;
+ dstlen = SZINT/SZCHAR;
+ }
+ srctype = dsttype;
+ srclen = dstlen;
+ }
+
+ if (srclen < dstlen) {
+ if (srctype == CHAR && dsttype == USHORT && dst->in.op == REG) {
+ /* (unsigned short) c; => sign extend to 16 bits */
+ putstr("cvtbl\t");
+ adrput(src);
+ putstr(",-(sp)\n\tmovzwl\t2(sp),");
+ adrput(dst);
+ putstr("\n\tmovab\t4(sp),sp");
+ if (forcc) {
+ /* inverted test */
+ putstr("\n\tcmpl\t$0,");
+ adrput(dst);
+ }
+ return;
+ }
+ genconv(ISUNSIGNED(srctype),
+ srclen, dst->in.op == REG ? SZINT/SZCHAR : dstlen,
+ src, dst);
+ return;
+ }
+
+ if (srclen > dstlen && dst->in.op == REG) {
+ /* if dst is a register, the result must look like an int */
+ if (src->in.op == REG) {
+ if (ISUNSIGNED(dsttype)) {
+ val = (1 << dstlen * SZCHAR) - 1;
+ if (src->tn.rval == dst->tn.rval)
+ /* conversion in place */
+ printf("andl2\t$%#x,", val);
+ else {
+ printf("andl3\t$%#x,", val);
+ adrput(src);
+ putchar(',');
+ }
+ adrput(dst);
+ return;
+ }
+ /*
+ * Sign extension in register can also be
+ * accomplished by shifts, but unfortunately
+ * shifts are extremely slow, due to the lack
+ * of a barrel shifter.
+ */
+ putstr("pushl\t");
+ adrput(src);
+ putstr("\n\tcvt");
+ prlen(dstlen);
+ printf("l\t%d(sp),", SZINT/SZCHAR - dstlen);
+ adrput(dst);
+ putstr("\n\tmovab\t4(sp),sp");
+ if (forcc) {
+ /* inverted test */
+ putstr("\n\tcmpl\t$0,");
+ adrput(dst);
+ }
+ return;
+ }
+ tmp = talloc();
+ if ((src->in.op == UNARY MUL &&
+ ((src->in.left->in.op == NAME ||
+ (src->in.left->in.op == ICON)))) ||
+ (src->in.op == OREG && !R2TEST(src->tn.rval))) {
+ /* we can increment src's address & pun it */
+ *tmp = *src;
+ tmp->tn.lval += srclen - dstlen;
+ } else {
+ /* we must store src's address */
+ *tmp = *dst;
+ putstr("mova");
+ prlen(srclen);
+ putchar('\t');
+ adrput(src);
+ putchar(',');
+ adrput(tmp);
+ putstr("\n\t");
+ tmp->tn.op = OREG;
+ tmp->tn.lval = srclen - dstlen;
+ }
+ genconv(ISUNSIGNED(dsttype), dstlen, SZINT/SZCHAR, tmp, dst);
+ tmp->in.op = FREE;
+ return;
+ }
+
+ genconv(ISUNSIGNED(dsttype),
+ srclen, dst->in.op == REG ? SZINT/SZCHAR : dstlen,
+ src, dst);
+}
+
+genconv(usrc, srclen, dstlen, src, dst)
+ int usrc;
+ register int srclen, dstlen;
+ NODE *src, *dst;
+{
+ if (srclen != dstlen) {
+ if (usrc && srclen < dstlen)
+ putstr("movz");
+ else
+ putstr("cvt");
+ prlen(srclen);
+ } else
+ putstr("mov");
+ prlen(dstlen);
+ putchar('\t');
+ adrput(src);
+ putchar(',');
+ adrput(dst);
+}
+