/* $Id: saturn.c,v 1.1.1.10 1995/07/11 22:17:31 alex Exp $ */

/* Subroutines for insn-output.c for GNU compiler.  Saturn version.
   This port, done by Alex T. Ramos <ramos@engr.latech.edu> in 1994,
   is the first 4-bit port of GNU CC.

   Copyright (C) 1987, 1992 Free Software Foundation, Inc

This file is part of GNU CC.

GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <stdio.h>
#include "config.h"
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "insn-flags.h"
#include "output.h"
#include "real.h"
#include "flags.h"

/* Under BSDi, the standard assert() is broken */
#define ASSERT(CONDITION) if(!(CONDITION)) abort();

/* RCS identification info */
	char *saturn_md_version = "";
	char *saturn_c_version  =
		"$Id: saturn.c,v 1.1.1.10 1995/07/11 22:17:31 alex Exp $";

/* Internal gcc variables */

	extern char *reg_names[];
	extern int   optimize;


/* Exported global variables */

	int parm_alloc_order[N_REG_PARMS] = PARM_ALLOC_ORDER;

	rtx cmp_op0 = 0, cmp_op1 = 0;

/* Exported functions */

int
branch_target(x)
     rtx x;
{
    return 1;
}

int
arithmetic_register(x)
     rtx x;
{
    if(REG_P(x) && REGNO(x)>=REG_A && REGNO(x)<=REG_D)
	return 1;
    else
	return 0;
}

/* Return true if OP is a CONST_INT equal to 1.
   This is an acceptable operand for inc or dec instructions.  */
int
immediate_one_operand (op, mode)
  rtx op;
  enum machine_mode mode;
{
  return (GET_CODE (op) == CONST_INT
	  && abs (INTVAL (op)) == 1);
}

/* Return true if OP is a CONST_INT, with OP <= 16.
   This is an acceptable operand for constant add instructions.  */
int
immediate_nib_p1_operand (op, mode)
  rtx op;
  enum machine_mode mode;
{
  return (GET_CODE (op) == CONST_INT
	  && INTVAL (op) != 0
	  && abs (INTVAL (op)) <= 16);
}

int
function_arg_regno_p(regno)
     int regno;
{
    int i;
    
    for (i=0; i<N_REG_PARMS; ++i) {
	if (regno == parm_alloc_order[i])
	    return 1;
    }
    return 0;
}

char *
mode_fieldspec(x)
     rtx x;
{
  switch (GET_MODE (x))
   {
      case QImode:
	return 0;
      case HImode: 
	return "b";
      case SImode:
	return 0;
      case PDImode:
	return "a";
      case DImode:
	return "wp";
      case TImode:
	return "w";
      default:
	abort();
    }
}

char *
reg_p_for_mode(x)
     rtx x;
{
  switch (GET_MODE (x))
   {
      case QImode:
        return "0";
      case HImode:
        return "1";
      case SImode:
        return "3";
      case PDImode:
        return "4";
      case DImode:
        return "7";
      case TImode:
        return "15";
      default:
        abort();
    }
}

char *
mode_spec (x)
     rtx x;
{
  switch (GET_MODE (x))
   {
      case QImode:
        return "qi";
      case HImode:
        return "hi";
      case SImode:
        return "si";
      case PDImode:
        return "pdi";
      case DImode:
        return "di";
      case TImode:
        return "ti";
      default:
        abort();
    }
}

void
output_lib_cmp (insn, x, y)
  rtx insn, x, y;
{
  rtx compare = next_cc0_user (insn);
  enum rtx_code code = GET_CODE (XEXP (XEXP (PATTERN (compare), 1), 0));
  enum machine_mode mode = GET_MODE (x);
  char *libcall;
  rtx l[1];

  switch (mode)
    {
      case DFmode:
        switch (code)
	  {
	    case EQ:
	      libcall = "__eqdf2";
	      break;

	    case NE:
	      libcall = "__nedf2";
	      break;

	    case GE:
	      libcall = "__gedf2";
	      break;

	    case GT:
	      libcall = "__gtdf2";
	      break;

	    case LE:
	      libcall = "__ledf2";
	      break;

	    case LT:
	      libcall = "__ltdf2";
	      break;

	    default:
	      abort ();
	  }
	break;

      default:
	abort ();
    }

  l[0] = gen_rtx (SYMBOL_REF, Pmode, libcall);
  assemble_external_libcall (l[0]);
  output_asm_insn ("jsr %0", l);
}

char *
cmp_jmp (op, label, reversed)
     rtx op;
     rtx label;
     int reversed;
{
    enum cmp_type                 {  gt,   eq,   ge,   lt,   ne,   le  };
    enum cmp_type cmp_rev[]     = {  le,   ne,   lt,   ge,   eq,   gt  };
    static char *cmp_str[] 	= { "gt", "eq", "ge", "lt", "ne", "le" };

    static char 		temp[256];
    enum rtx_code 		code 		= GET_CODE (op);
    enum machine_mode 		mode   		= GET_MODE (XEXP (op, 0));
    static int 			jid 		= 0;
    int 			type, is_signed;
    rtx 			x[3];

    *temp = '\0';

    if (GET_MODE_CLASS (GET_MODE (cmp_op0)) == MODE_FLOAT) {

	sprintf (temp, "bc%s\t.J%d\n\tjmp %%0\n.J%d",
		 reversed ? "s" : "c", jid, jid);
	jid++;
	goto done;
    }

    switch(code) {
    case GT:        type = gt; is_signed = 1; break;
    case LT:        type = lt; is_signed = 1; break;
    case GE:	    type = ge; is_signed = 1; break;
    case LE:	    type = le; is_signed = 1; break;
    case GEU:       type = ge; is_signed = 0; break;
    case GTU:       type = gt; is_signed = 0; break;
    case LEU:       type = le; is_signed = 0; break;
    case LTU:       type = lt; is_signed = 0; break;
	/* Sign doesn't matter in equality test, so treat as unsigned. */
    case EQ:        type = eq; is_signed = 0; break;
    case NE:        type = ne; is_signed = 0; break;
    default:  	    abort();
    }

    if(!is_signed) {
	char *fs = mode_fieldspec (cmp_op0);

	if (fs) {
	    sprintf (temp, "b%s.%s %%1,%%2,.J%d\n\tjmp %%0\n.J%d",
		     reversed ? cmp_str[type] : cmp_str[cmp_rev[type]],
		     fs, jid, jid);
	}
	else {
	    fs = reg_p_for_mode (cmp_op0);
	    sprintf (temp, "move.1 #%s,p\n\t"
			   "b%s.wp %%1,%%2,.J%d\n\t"
			   "move.1 #7,p\n\t"
			   "jmp %%0\n"
			   ".J%d\n\t"
			   "move.1 #7,p",
		     fs, reversed ? cmp_str[type] : cmp_str[cmp_rev[type]],
		     jid, jid);
	}

	jid++;
	goto done;
    }
    
    /* Signed comparison */

    if(!cmp_op1) {	/* against zero */

	int msn = GET_MODE_SIZE (GET_MODE (cmp_op0)) - 1;
	int lose = jid++;
	int lose1 = jid++;
	int win  = jid++;
	int carry_wins;

	static char *restore_clean = "\tlsr.p #1,%1";
	static char *restore_full  = "\tnot.p %1\n\tlsr.p #1,%1\n\tnot.p %1";

	if (msn != 7)
	    sprintf(temp, "move.1 #%d,p\n\t", msn);

	if (reversed) {
	    reversed = 0;
	    type = cmp_rev[type];
	}

	if (type == gt || type == le) {
	    sprintf(temp+strlen(temp),	"beq.wp %%1,0,.J%d\n\t",
		    (type==le) ? win : lose1);
	}

	carry_wins = (type == lt || type == le);

	sprintf(temp+strlen(temp),	"add.p %%1,%%1\n\t"
		                        "bc%c .J%d\n",
		carry_wins ? 'c' : 's', lose);

	sprintf(temp+strlen(temp),	".J%d\n", win);

	if(carry_wins)
	    strcat(temp, restore_full);
	else
	    strcat(temp, restore_clean);

	if(msn != 7)
	    sprintf(temp+strlen(temp), "\n\tmove.1 #7,p");

	sprintf(temp+strlen(temp),      "\n\tjmp %%0\n"
		                        ".J%d\n",  lose);

	if(carry_wins)
	    strcat(temp, restore_clean);
	else
	    strcat(temp, restore_full);

	sprintf(temp+strlen(temp),	"\n.J%d", lose1);

	if(msn != 7)
	    sprintf(temp+strlen(temp), "\n\tmove.1 #7,p");
    }
    else {			/* sign-compare 2 registers */
	int   lose = jid++;
	int   msn = GET_MODE_SIZE (GET_MODE (cmp_op0)) - 1;

	/* here, reversed is not */
	char* operator = reversed ? cmp_str[type] : cmp_str[cmp_rev[type]];

	if(msn != 7)
	    sprintf(temp,		"move.1 #%d,p\n\t", msn);

	sprintf(temp+strlen(temp),	"add.p #8,%%1\n\t"
		                        "add.p #8,%%2\n\t"
		                        "b%s.wp %%1,%%2,.J%d\n\t",
		operator, lose);

	sprintf(temp+strlen(temp),	"sub.p #8,%%1\n\t"
		                        "sub.p #8,%%2\n\t");
	if(msn != 7) 
	    sprintf(temp+strlen(temp),	"move.1 #7,p\n\t");
	
	sprintf(temp+strlen(temp),	"jmp %%0\n"
			                ".J%d\n\t", lose);

	sprintf(temp+strlen(temp),	"sub.p #8,%%1\n\t"
		                        "sub.p #8,%%2");
	if(msn != 7) 
	    sprintf(temp+strlen(temp),	"\n\tmove.1 #7,p");
    }
	    
 done:
    x[0]=label;
    x[1]=cmp_op0;
    x[2]=cmp_op1 ? cmp_op1 : const0_rtx;
    output_asm_insn (temp, x);

    return "";
}


char	*saturn_add_unroll = "64";	/* -munroll-adds-above-64 */

char *

expand_inline_add_const (reg, cons)
     rtx reg;
     int cons;
{
    char 	template[256];
    int		unroll;

    if (1 != sscanf (saturn_add_unroll, "%d", &unroll)) {
	fatal ("-munroll-adds-above-XXX requires an integer argument");
    }

    if (abs(cons) > unroll) {
	sprintf (template, "push\n\tmove.1 #0,p\n\tmove.5 #$%x,c\n\t"
			   "exg.a a,%%0\n\tadd.a c,a\n\texg.a a,%%0\n\t"
			   "move.1 #7,p\n\tpop", cons & 0xfffff);
	output_asm_insn (template, &reg);
	return "";
    }
    if (cons>16) {
        expand_inline_add_const (reg,16);
        expand_inline_add_const (reg,cons-16);
        return "";
    }
    else
    if (cons < -16) {
        expand_inline_add_const (reg,-16);
        expand_inline_add_const (reg,cons+16);
        return "";
    }
    else
    if (cons == 0) {
        return "";
    }

    sprintf (template, "%s.a #%d,%%0", cons>0 ? "add" : "sub", abs(cons));
    output_asm_insn (template, &reg);
    return "";
}

char *
expand_inline_add (reg, add)
  rtx reg, add;
{
  char template [256];
  rtx  op[2];

  switch (GET_CODE (add))
    {
      case CONST:
	expand_inline_add (reg, XEXP (add, 0));
	break;
      case CONST_INT:
	expand_inline_add_const (reg, INTVAL (add));
	break;
      case LABEL_REF:
      case SYMBOL_REF:
	sprintf (template, "push\n\tmove.1 #0,p\n\tmove.ao #%%1,c\n\t"
			   "exg.a a,%%0\n\tadd.a c,a\n\texg.a a,%%0\n\t"
			   "move.1 #7,p\n\tpop");
	op[0] = reg;
	op[1] = add;
	output_asm_insn (template, op);
	break;
      case PLUS:
	expand_inline_add (reg, XEXP (add, 0));
	expand_inline_add (reg, XEXP (add, 1));
	break;
      default:
	abort ();
    }
  return "";
}

void
saturn_print_operand_address (file, addr)
     FILE *file;
     register rtx addr;
{
    switch (GET_CODE(addr)) {
    case REG:
	fprintf (file, "(%s)", reg_names[REGNO (addr)]);
	break;
    case SYMBOL_REF:
	assemble_name (file, XSTR(addr,0));
	break;
    case PLUS:
	warning ("compiling without optimization not supported");
	fprintf (file, "(%s+%d)", reg_names[REGNO (XEXP(addr,0))],
		                  INTVAL (XEXP (addr,1)));
	break;
    default:
	abort();
    }
}


/* Print an instruction operand X on file FILE.
   CODE is the code from the %-spec that requested printing this operand;
   if `%z3' was used to print operand 3, then CODE is 'z'.  */

void 
saturn_print_operand (file, x, code)
     FILE *file;
     rtx   x;
     char  code;
{

    if(code == 'm') {
	/*
         * print out the next available asm mode for the machine mode
	 * of this operand. This is for instructions that don't care
	 * about bits outside the used width.
         */
	if(GET_MODE(x) == SImode)
	    fputs("a", file);
	else
	    fputs(mode_fieldspec(x), file);
    }
    else
    switch (GET_CODE (x))
    {
    case REG:
	fprintf (file, "%s", reg_names[REGNO (x)]);
	break;
    case MEM:
	output_address (XEXP (x, 0));
	break;
    case LABEL_REF:
	saturn_print_operand (file, XEXP (x, 0), code);
	break;
    case CONST:
	saturn_print_operand (file, XEXP (x, 0), code);
	break;
      case CONST_INT:
	switch(code) {
	case 'P':
	    if(CONSTANT_P(x))
		fputc('#', file);
	    fprintf (file, "%d", INTVAL(x));
	    break;
	case 'n':
	    fprintf (file, "%d", -INTVAL(x));
	    break;
	case 'L':
	    fprintf (file, "$%x00000000", INTVAL(x) & 0xFFFFFFFFU);
	    break;
	case 'A':
	    fprintf (file, "$%05x", INTVAL(x) & 0xfffff);
	    break;
	case 'B':
	    fprintf (file, "$%08x", INTVAL(x) & 0xffffffff);
	    break;
	case 'C':
	    fprintf (file, "$%04x", INTVAL(x) & 0xffff);
	    break;
	case 'D':
	    fprintf (file, "$%02x", INTVAL(x) & 0xff);
	    break;
	default:
	    fprintf (file, "%d", INTVAL(x));
	    break;
	}
	break;
    case SYMBOL_REF:
	assemble_name (file, XSTR(x,0));
	break;
    case CODE_LABEL: 
        {
	  char label[32];
	  ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER(x));
	  assemble_name (file, label);
        }
	break;
      case PLUS:
	saturn_print_operand (file, XEXP (x, 0), code);
	fprintf (file, "+");
	saturn_print_operand (file, XEXP (x, 1), code);
	break;
    default:
	abort();
    }
}

void
saturn_output_addr_const (file, x, size)
     FILE *file;
     rtx   x;
     int   size;
{
  char buf[256];

restart:
  switch (GET_CODE (x))
    {
      case SYMBOL_REF:
	assemble_name (file, XSTR (x, 0));
	break;

      case LABEL_REF:
	ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (XEXP (x, 0)));
	assemble_name (file, buf);
	break;

      case CODE_LABEL:
	ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
	assemble_name (file, buf);
	break;

      case CONST_INT:
	switch (GET_MODE (x))
	  {
	    case VOIDmode:
	      switch (size)
		{
		  case 1:
		    goto size_for_qi;
		  case 2:
		    goto size_for_hi;
		  case 4:
		    goto size_for_si;
		  case 5:
		    goto size_for_pdi;
		  case 8:
		    goto size_for_di;
		  case 16:
		    goto size_for_ti;
		  default:
	            output_operand_lossage ("unknown size const_int operand");
		    break;
		}
	      break;
	    size_for_qi:
	    case QImode:
	      if (INTVAL (x) < 0)
	        fprintf (file, "$%x", INTVAL (x) & 0xf);
	      else
	        fprintf (file, "%d", INTVAL (x));
	      break;
	    size_for_hi:
	    case HImode:
	      if (INTVAL (x) < 0)
	        fprintf (file, "$%x", INTVAL (x) & 0xff);
	      else
	        fprintf (file, "%d", INTVAL (x));
	      break;
	    size_for_si:
	    case SImode:
	      if (INTVAL (x) < 0)
	        fprintf (file, "$%x", INTVAL (x) & 0xffff);
	      else
	        fprintf (file, "%d", INTVAL (x));
	      break;
	    size_for_pdi:
	    case PDImode:
	      if (INTVAL (x) < 0)
	        fprintf (file, "$%x", INTVAL (x) & 0xfffff);
	      else
	        fprintf (file, "%d", INTVAL (x));
	      break;
	    size_for_di:
	    case DImode:
	      if (INTVAL (x) < 0)
	        fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
		         "$%x",
#else
		         "$%lx",
#endif
		         INTVAL (x));
	      else
	        fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
		         "%d",
#else
		         "%ld",
#endif
		         INTVAL (x));
	      break;

	    default:
	      output_operand_lossage ("unknown mode in const_int operand");
	      break;

	  }
	break;

      case CONST:
	saturn_output_addr_const (file, XEXP (x, 0), size);
	break;

      size_for_ti:
      case CONST_DOUBLE:
	if (GET_MODE (x) == VOIDmode)
	  {
	    if (CONST_DOUBLE_HIGH (x))
	      fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
		       "$%x%08x",
#else
		       "$%lx%08lx",
#endif
		       CONST_DOUBLE_HIGH (x), CONST_DOUBLE_LOW (x));
	    else if (CONST_DOUBLE_LOW (x) < 0)
	      fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
		       "$%x",
#else
		       "$%lx",
#endif
		       CONST_DOUBLE_LOW (x));
	    else
	      fprintf (file,
#if HOST_BITS_PER_WIDE_INT == HOST_BITS_PER_INT
		       "%d",
#else
		       "%ld",
#endif
		       CONST_DOUBLE_LOW (x));
	  }
	else
	  output_operand_lossage ("floating constant misused");
	break;

      case PLUS:
	if (GET_CODE (XEXP (x, 0)) == CONST_INT)
	  {
	    saturn_output_addr_const (file, XEXP (x, 1), size);
	    if (INTVAL (XEXP (x, 0)) >= 0)
	      fprintf (file, "+");
	    saturn_output_addr_const (file, XEXP (x, 0), size);
	  }
	else
	  {
	    saturn_output_addr_const (file, XEXP (x, 0), size);
	    if (INTVAL (XEXP (x, 1)) >= 0)
	      fprintf (file, "+");
	    saturn_output_addr_const (file, XEXP (x, 1), size);
	  }
	break;

      case MINUS:
	x = simplify_subtraction (x);
	if (GET_CODE (x) != MINUS)
	  goto restart;

	saturn_output_addr_const (file, XEXP (x, 0), size);
	fprintf (file, "-");
	if (GET_CODE (XEXP (x, 1)) == CONST_INT
	    && INTVAL (XEXP (x, 1)) < 0)
	  {
	    fprintf (file, ASM_OPEN_PAREN);
	    saturn_output_addr_const (file, XEXP (x, 1), size);
	    fprintf (file, ASM_CLOSE_PAREN);
	  }
	else
	  saturn_output_addr_const (file, XEXP (x, 1), size);
	break;

      case ZERO_EXTEND:
      case SIGN_EXTEND:
	saturn_output_addr_const (file, XEXP (x, 0), size);
	break;

      default:
	output_operand_lossage ("invalid operand expression");
	break;
    }
}


enum reg_class
secondary_reload_class (class, mode, x)
     enum reg_class class;
     enum machine_mode mode;
     rtx  x;
{
    ASSERT(!REG_P(x) || REGNO(x)>=0);
    ASSERT(class>=0 && class<LIM_REG_CLASSES);
    ASSERT(class != NO_REGS);

    switch (GET_CODE(x)) {
    case REG:
    case SUBREG:
	switch (REGNO(x)) {
	/* only D and pseudos require C; all others can be moved through A */
	case REG_C:
	    return NO_REGS;
	case REG_A:
	    /* need REG_C if this class might contain REG_D */
	    return 
		TEST_HARD_REG_BIT(reg_class_contents[class], REG_D) ?
		CREG : NO_REGS;
	case REG_D0: case REG_D1: 
	case REG_B:  case REG_R0:
	case REG_R1: case REG_R2:
	case REG_R3: case REG_R4:
	    /* for these regs, CLASS is more important */
	    break;
	case REG_D:
	case REG_HS:
	default: /* pseudos fall here */
	    return class==CREG ? NO_REGS : CREG;
	}
	break;
    case SYMBOL_REF:
    case LABEL_REF:
    case CONST:
    case CONST_INT: {
	switch (class) {
	case AREG: case CREG: case ACREG:
#	if 0
	    /* this did not work too well. */
	    if ( abs(INTVAL (x)) <= 0xFFFFF) {
		return DnREG;
	    }
#	endif
	case DnREG: case PTR_REGS:
	    return NO_REGS;
	case DREG: case GENERAL_REGS:
	    return CREG;
	case RnREG: case BREG: case T_REGS:
	    return ACREG;
	default:
	    abort();
	}
    }
    case MEM:
	break;
    default:
	/* unexpected X value */
	abort ();
    }
    switch (class) {
    case GENERAL_REGS:
    case DREG:
	return CREG;
    case AREG: case CREG: case ACREG:
	return NO_REGS;
    case PTR_REGS:
    case DnREG:
    case RnREG:
    case BREG: case T_REGS:
	return ACREG;
    default:
	abort();
    }
}


enum reg_class
preferred_reload_class (x,class)
     rtx		x;
     enum reg_class 	class;
{
    return class;
}

#define MAGIC 10  /* make sure we notice here when new classes are added */

static	int	class_move_cost[N_REG_CLASSES-1][N_REG_CLASSES-1] = {
    	/*  a  b  c  d  q  D  R  -  hs D  t  g  X */
/* a */  {  2, 2, 2, 4, 4, 2, 2, 4, 9, 2, 2, 4, 4 },
/* b */  {  2, 2, 2, 4, 4, 4, 4, 4, 9, 4, 2, 4, 4 }, 
/* c */  {  2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, 2, 2 },
/* d */  {  4, 4, 2, 2, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* q */  {  4, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* D */  {  2, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* R */  {  2, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* - */  {  4, 4, 2, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4 },
/* - */  {  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 },
/* D */  {  2, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* t */  {  2, 2, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* g */  {  4, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, 4 },
/* X */  {  4, 4, 2, 4, 4, 4, 4, 4, 9, 4, 4, 4, MAGIC }
};


int	register_move_cost(from, to) 
     int from, to;
{
    ASSERT(class_move_cost[ALL_REGS-1][ALL_REGS-1] == MAGIC);

    if (from==0 || to==0) {
	return 8;	/* this case occurs; meaning unknown */
    }
    return class_move_cost[from-1][to-1];
}


	         

void
function_prologue(file, local_storage_size)       
     FILE *file;
     int local_storage_size;
{
    int	i;
	
    fprintf (file, "\t;; regs used: ");
    for (i=0; i<FIRST_PSEUDO_REGISTER; ++i) {
	if (regs_ever_live[i]) {
	    fprintf (file, " %s", reg_names[i]);
	}
    }
    fprintf (file, "\n\tmove.a #%d,d0\n", local_storage_size + 10);
    fputs ("\tjsr ___prologue\n", file);
}

void
function_epilogue(file, local_storage_size)
     FILE *file;
     int local_storage_size;
{
    fputs ("\tjmp ___epilogue\n", file);
}


rtx
find_add_insn(rtx insn, rtx stop)
{
    while (insn && insn!=stop) {
	if (GET_CODE(insn)==INSN &&
	    GET_CODE(PATTERN(insn))==SET &&
	    REG_P(SET_DEST(PATTERN(insn))) &&
	    REGNO(SET_DEST(PATTERN(insn)))==FRAME_POINTER_REGNUM &&
	    GET_CODE(SET_SRC(PATTERN(insn)))==PLUS) {
	    return insn;
	}
	insn = NEXT_INSN(insn);
    }
    return NULL;
}

rtx
find_float_cmp_insn (rtx insn, rtx stop)
{
  rtvec rtv;
  rtx comp;
 
  while (insn && insn != stop)
    {
      if (GET_CODE (insn) == INSN &&
          GET_CODE (PATTERN (insn)) == PARALLEL &&
          (rtv = XVEC (PATTERN (insn), 0)) &&
          GET_CODE (RTVEC_ELT (rtv, 0)) == SET &&
          (comp = XEXP (RTVEC_ELT (rtv, 0), 1)) &&
          GET_CODE (comp) == COMPARE &&
          GET_CODE (XEXP (comp, 0)) == REG &&
          GET_MODE_CLASS (GET_MODE (XEXP (comp, 0))) == MODE_FLOAT)
        {
          return insn;
        }
      insn = NEXT_INSN (insn);
    }
  return NULL;
}

int
frame_pointer_used_between_p (insn, stop)
     rtx insn, stop;
{
    if (reg_used_between_p(frame_pointer_rtx,insn,stop)) {
	return 1;
    }
    if (find_float_cmp_insn (insn, stop)) {
	return 1;
    }
    while ( (insn = NEXT_INSN(insn)) && insn != stop) {
	if (GET_CODE (insn) == CALL_INSN)
	    return 1;
	if (GET_CODE (insn) == CODE_LABEL)
	    return 1;
    }
    return 0;
}

void
optimize_adjacent_addition()
{
    int		bb;
    rtx		insn;

    extern	int n_basic_blocks;
    extern	rtx *basic_block_head, *basic_block_end;

    for (bb=0; bb<n_basic_blocks; ++bb) {	
	insn = basic_block_head[bb];
	while (insn = find_add_insn(insn, basic_block_end[bb])) {
	    rtx second = find_add_insn(NEXT_INSN(insn), basic_block_end[bb]);
	    if (second && !frame_pointer_used_between_p(insn, second)) {
		XEXP(SET_SRC(PATTERN(insn)),1) = gen_rtx 
		    (CONST_INT,
		     VOIDmode,
		     INTVAL(XEXP(SET_SRC(PATTERN(insn)),1))
		     + INTVAL(XEXP(SET_SRC(PATTERN(second)),1)));
		delete_insn(second);
	    }
	    insn = NEXT_INSN(insn);
	}
    }
}

void
fake_indexed_addressing(insn)
     rtx insn;
{
    int	 xop;
    rtx	 subreg, trunc, mem, sub;
    rtx  base_reg, index, set[2];

    do {
        if (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != SET) 
	    continue;

	for (xop=0; 
	     (mem = XEXP (PATTERN(insn), xop)),
	     xop<=1 && GET_CODE (mem) != MEM;
	     ++xop) {
	    /* empty loop */
	}

        if (xop<2 && GET_CODE (XEXP (mem,0)) == PLUS)
	{
	    base_reg = XEXP (XEXP (mem, 0), 0);
	    index    = XEXP (XEXP (mem, 0), 1);
	
	    emit_insn_before (gen_addpdi3 (base_reg, base_reg, index), insn);
	    index	= gen_rtx (CONST_INT, VOIDmode, -INTVAL(index));
	    emit_insn_after  (gen_addpdi3 (base_reg, base_reg, index), insn);

	    set[xop]  = gen_rtx (MEM, GET_MODE(mem), base_reg);
	    set[!xop] = XEXP (PATTERN(insn), !xop);

	    emit_insn_after (gen_rtx (SET, VOIDmode, set[0], set[1]), insn);
	    delete_insn (insn);
	}
	else {			/* xop == 2 (no MEM found) */

	    trunc = subreg = XEXP (PATTERN(insn), 1);

	    if (GET_CODE (subreg) == SUBREG
		&& GET_CODE (mem = XEXP (subreg, 0)) == MEM
		&& GET_CODE (XEXP (mem,0)) == PLUS)

	    {
		base_reg = XEXP (XEXP (mem, 0), 0);
		index    = XEXP (XEXP (mem, 0), 1);
	
		emit_insn_before (gen_addpdi3 (base_reg, base_reg, index), 
				  insn);
		index	= gen_rtx (CONST_INT, VOIDmode, -INTVAL(index));
		emit_insn_after  (gen_addpdi3 (base_reg, base_reg, index),
				  insn);

		sub = gen_rtx (MEM, GET_MODE(mem), base_reg);

		set[0] = XEXP (PATTERN (insn), 0);
		set[1] = gen_rtx (SUBREG, GET_MODE(subreg), sub, XINT (subreg,
								       1));

		emit_insn_after (gen_rtx (SET, VOIDmode, set[0], set[1]),
				 insn);
		delete_insn (insn);
	    }
	    else
	    if (GET_CODE (trunc) == TRUNCATE
		&& GET_MODE (trunc) == PDImode
		&& GET_CODE (mem = XEXP (trunc, 0)) == MEM
		&& GET_CODE (XEXP (mem,0)) == PLUS) 
	    {
		base_reg = XEXP (XEXP (mem, 0), 0);
		index    = XEXP (XEXP (mem, 0), 1);
	
		emit_insn_before (gen_addpdi3 (base_reg, base_reg, index),
				  insn);
		index	= gen_rtx (CONST_INT, VOIDmode, -INTVAL(index));
		emit_insn_after  (gen_addpdi3 (base_reg, base_reg, index),
				  insn);

		sub = gen_rtx (MEM, GET_MODE(mem), base_reg);

		set[0] = XEXP (PATTERN (insn), 0);
		set[1] = gen_rtx (TRUNCATE, GET_MODE(trunc), sub);

		emit_insn_after (gen_rtx (SET, VOIDmode, set[0], set[1]),
				 insn);
		delete_insn (insn);
	    }
	}
    } 
    while(insn = NEXT_INSN(insn));

    if (optimize) {
	optimize_adjacent_addition();
    }
}


char *
output_shift_insn (operands, type, sense)
     rtx operands[3];
     char type, sense;
{
    int 	n;
    char 	tem[200];
    char	libcall[20];
    rtx		l;
    char	*fs = mode_fieldspec (operands[0]);
    char	*p = 0;

    if (!fs)
      {
	p = reg_p_for_mode (operands[0]);
	fs = "wp";
      }
    if (GET_CODE (operands[2]) == CONST_INT)
      {	
	n = INTVAL (operands[2]);
	if (n <= 0)
	  return "";
	if (p)
	  {
	    sprintf (tem, "move.1 #%s,p", p);
	    output_asm_insn (tem, operands);
	  }
	while (n % 4)
	  {
	    n--;
	    sprintf (tem, "ls%c.%s #1,%%0", sense, fs);
	    output_asm_insn (tem, operands);
	  }
	while (n)
	  {
	    n -= 4;
	    sprintf (tem, "ls%c.%s #4,%%0", sense, fs);
	    output_asm_insn (tem, operands);
	  }
	if (p)
	  {
	    sprintf (tem, "move.1 #7,p", p);
	    output_asm_insn (tem, operands);
	  }
      }
    else
     {
	sprintf (libcall, "__%csh%c%s2", type, sense, mode_spec (operands[0]));
	l = gen_rtx (SYMBOL_REF, Pmode, libcall);
	assemble_external_libcall (l);
	sprintf (tem, "jsr %%1 ; %s-shift '%%0' for '%%2' bits",
		 (sense == 'r') ? "right" : "left");
	operands[1] = l;
	output_asm_insn (tem, operands);
     }
    return "";
}

/*
 * Floating point emulation for the HP48.
 */

#include "tree.h"

#define DOMAIN		1
#define SING		2
#define OVERFLOW	3
#define UNDERFLOW	4
#define TLOSS		5
#define PLOSS		6
#define INVALID		7

#define NMSGS		8

static char *errmsg[NMSGS] =
{
  "unknown",
  "domain",
  "singularity",
  "overflow",
  "underflow",
  "total loss of precision",
  "partial loss of precision",
  "invalid operation"
};

int merror = 0;
extern int merror;

struct saturn_ereal {
  char               sign;
  unsigned long long mhi;
  unsigned long long mlo;
  char               esign;
  unsigned short     exp;
};

void
saturn_real_error (name, code)
  char *name;
  int code;
{
  char errstr[80];

  /* Display string passed by calling function, which is supposed to be
     the name of the function in which the error occurred.

     Display error message defined by the code argument. */

  if ((code < 0) || (code >= NMSGS))
    code = 0;
  sprintf (errstr, " %s %s error", name, errmsg[code]);
  if (extra_warnings)
    warning (errstr);
  merror = code + 1;
}

void
saturn_real_clear (r)
  REAL_VALUE_TYPE *r;
{
  r->sign = 1;
  r->mant = 0;
  r->esign = 1;
  r->exp = 0;
}

void
saturn_real_copy (src, dst)
  REAL_VALUE_TYPE *src;
  REAL_VALUE_TYPE *dst;
{
  bcopy ((char *)src, (char *)dst, sizeof (REAL_VALUE_TYPE));
}

REAL_VALUE_TYPE
saturn_real_normalize (e, mode)
  struct saturn_ereal e;
  enum machine_mode mode;
{
  REAL_VALUE_TYPE x;
  unsigned long long t;
  long exp;
  long max_exp;

  if (e.mhi == 0 && e.mlo == 0)
    {
      exp = 0;
      goto pack;
    }

  exp = e.esign * e.exp;

  while (e.mhi < 100000000000000ULL)
    {
      /* scale left */
      t = e.mlo / 1000000000000000ULL;
      e.mlo = e.mlo % 1000000000000000ULL;
      e.mlo *= 10;
      e.mhi *= 10;
      e.mhi += t;
      exp -= 1;
    }

  do
    {
      while (e.mhi >= 1000000000000000ULL)
        {
          /* scale right */
          e.mlo /= 10;
          t = e.mhi % 10;
          e.mlo += t * 1000000000000000ULL;
          e.mhi /= 10;
          exp += 1;
        }

      switch (mode)
        {
          case SFmode:
          case DFmode:
            t = e.mhi % 1000;
	    t /= 100;
            if (t >= 5)
              e.mhi += 1000;
	    e.mhi -= e.mhi % 1000;
	    break;
          default:
            t = e.mlo / 1000000000000000ULL;
            if (t >= 5)
              e.mhi += 1;
	    break;
        }
    }
  while (e.mhi >= 1000000000000000ULL);

  switch (mode)
    {
      case SFmode:
      case DFmode:
	max_exp = 499;
	break;
      default:
	max_exp = 49999;
	break;
    }

  if (abs (exp) > max_exp)
    {
      if (exp < 0)
	{
          saturn_real_error ("real_normalize", UNDERFLOW);
	  e.mhi = 100000000000000ULL;
	  exp = -max_exp;
	}
      else
	{
          saturn_real_error ("real_normalize", OVERFLOW);
	  switch (mode)
	    {
	      case SFmode:
	      case DFmode:
		e.mhi = 999999999999000ULL;
		break;
	      default:
		e.mhi = 999999999999999ULL;
		break;
	    }
	  exp = max_exp;
	}
    }

pack:
  x.sign = e.sign;
  x.mant = e.mhi;
  if (exp < 0)
    {
      x.esign = -1;
      x.exp = -exp;
    }
  else
    {
      x.esign = 1;
      x.exp = exp;
    }
  return x;
}

REAL_VALUE_TYPE
saturn_real_add (d1, d2)
  REAL_VALUE_TYPE d1;
  REAL_VALUE_TYPE d2;
{
  struct saturn_ereal e, tmp;
  unsigned long long f;
  int i, c, t;
  int sign, shift;
  long e1, e2;

  e1 = d1.esign * d1.exp;
  e2 = d2.esign * d2.exp;
  if (e1 < e2)
    {
      saturn_real_copy (&d1, &tmp);
      saturn_real_copy (&d2, &d1);
      saturn_real_copy (&tmp, &d2);
    }
  e.exp = d1.exp;
  e.esign = d1.esign;

  shift = e.exp - d2.exp;

  if (shift > 17)
    {
      e.sign = d1.sign;
      e.mhi = d1.mant;
      e.mlo = 0;
    }
  else
    {
      /* perform addition */

      sign = 0;
      if (d1.sign != d2.sign)
	sign = 1;

      e.sign = d1.sign;

      f = 1;
      for (i = shift + 1; i < 16; i++)
        f *= 10;

      c = 0;
      e.mlo = 0;
      for (i = 0; i < shift; i++)
	{
	  if (sign)
	    t = c - (d2.mant % 10);
	  else
	    t = c + (d2.mant % 10);
	  c = t / 10;
	  t = t % 10;
          e.mlo += t * f;
	  d2.mant /= 10;
	  f *= 10;
	}

      f = 1;
      e.mhi = 0;
      for (i = 0; i < 15; i++)
	{
	  t = d1.mant % 10;
	  if (sign)
	    t += c - (d2.mant % 10);
	  else
	    t += c + (d2.mant % 10);
	  c = t / 10;
	  t = t % 10;
	  e.mhi += t * f;
	  d1.mant /= 10;
	  d2.mant /= 10;
          f *= 10;
	}

      if (c != 0)
	{
	  c = c % 10;
	  if (sign)
	    e.sign = -e.sign;
	  e.mhi += c * f;
	}
    }
  
  /* normalize */
  return saturn_real_normalize (e, VOIDmode);
}

REAL_VALUE_TYPE
saturn_real_sub (d1, d2)
  REAL_VALUE_TYPE d1;
  REAL_VALUE_TYPE d2;
{
  d2 = saturn_real_negate (d2);
  return saturn_real_add (d1, d2);
}

REAL_VALUE_TYPE
saturn_real_mul (d1, d2)
  REAL_VALUE_TYPE d1;
  REAL_VALUE_TYPE d2;
{
  struct saturn_ereal e;
  long exp;
  unsigned long h1, l1;
  unsigned long h2, l2;
  unsigned long long t1, t2;

  exp = d1.esign * d1.exp + d2.esign * d2.exp + 2;
  if (exp < 0)
    {
      e.esign = -1;
      e.exp = -exp;
    }
  else
    {
      e.esign = 1;
      e.exp = exp;
    }

  e.sign = d1.sign * d2.sign;

  h1 = (unsigned long)(d1.mant / 100000000ULL);
  l1 = (unsigned long)(d1.mant % 100000000ULL);
  h2 = (unsigned long)(d2.mant / 100000000ULL);
  l2 = (unsigned long)(d2.mant % 100000000ULL);

  e.mhi = (unsigned long long)h1 * (unsigned long long)h2;
  e.mlo = (unsigned long long)l1 * (unsigned long long)l2;

  t1 = (unsigned long long)l1 * (unsigned long long)h2;
  e.mhi += t1 / 100000000ULL;
  e.mlo += (t1 % 100000000ULL) * 100000000ULL;
  if (e.mlo > 10000000000000000ULL)
    {
      e.mhi += e.mlo / 10000000000000000ULL;
      e.mlo = e.mlo % 10000000000000000ULL;
    }

  t2 = (unsigned long long)h1 * (unsigned long long)l2;
  e.mhi += t2 / 100000000ULL;
  e.mlo += (t2 % 100000000ULL) * 100000000ULL;
  if (e.mlo > 10000000000000000ULL)
    {
      e.mhi += e.mlo / 10000000000000000ULL;
      e.mlo = e.mlo % 10000000000000000ULL;
    }

  return saturn_real_normalize (e, VOIDmode);
}

REAL_VALUE_TYPE
saturn_real_div (d1, d2)
  REAL_VALUE_TYPE d1;
  REAL_VALUE_TYPE d2;
{
  struct saturn_ereal e;
  int i, t;
  long exp;

  e.sign = d1.sign * d2.sign;

  if (d1.mant == 0)
    {
      saturn_real_error ("real_div", SING);
      e.mhi = 999999999999999ULL;
      e.mlo = 0;
      e.esign = 1;
      e.exp = 49999;
      return saturn_real_normalize (e);
    }

  exp = d1.esign * d1.exp - d2.esign * d2.exp + 14;
  if (exp < 0)
    {
      e.esign = -1;
      e.exp = -exp;
    }
  else
    {
      e.esign = 1;
      e.exp = exp;
    }

  e.mhi = d1.mant / d2.mant;
  d1.mant -= e.mhi * d2.mant;

  e.mlo = 0;
  for (i = 0; i < 16; i++)
    {
      e.mlo *= 10;
      d1.mant *= 10;
      t = d1.mant / d2.mant;
      e.mlo += t;
      d1.mant -= t * d2.mant;
    }

  return saturn_real_normalize (e, VOIDmode);
}

REAL_VALUE_TYPE
saturn_real_from_ulong (unsigned HOST_WIDE_INT l)
{
  REAL_VALUE_TYPE x;
  int i;
  unsigned char data[15];

  for (i = 0; i < 16; i++)
    data[i] = 0;

  for (i = 0; i < 15; i++)
    {
      data[i] = l % 10;
      l /= 10;
      if (l == 0)
        break;
    }

  x.sign = 1;
  x.esign = 1;
  x.exp = i;

  x.mant = 0;
  for (i = x.exp; i >= 0; i--)
    {
      x.mant *= 10;
      x.mant += data[i];
    }
  for (i = x.exp + 1; i < 15; i++)
    x.mant *= 10;

  return x;
}

void
saturn_real_to_int (lo, hi, x)
  HOST_WIDE_INT *lo;
  HOST_WIDE_INT *hi;
  REAL_VALUE_TYPE x;
{
  /* UNFINISHED */

  *lo = 0;
  *hi = 0;
}

void
saturn_real_from_int (x, lo, hi)
  REAL_VALUE_TYPE *x;
  HOST_WIDE_INT lo;
  HOST_WIDE_INT hi;
{
  REAL_VALUE_TYPE one, df, dg;
  int sign;

  saturn_real_clear (&one);
  one.mant = 100000000000000ULL;
  sign = 0;
  if (hi < 0)
    {
      sign = 1;
      hi = ~hi;
      if (lo)
	lo = -lo;
      else
	hi += 1;
    }
  df = saturn_real_ldexp (one, HOST_BITS_PER_WIDE_INT);
  dg = saturn_real_from_ulong ((unsigned HOST_WIDE_INT)hi);
  dg = saturn_real_mul (dg, df);
  df = saturn_real_from_ulong ((unsigned HOST_WIDE_INT)lo);
  dg = saturn_real_add (dg, df);
  if (sign)
    dg = saturn_real_negate (dg);
  saturn_real_copy (&dg, x);
}

void
saturn_real_from_uint (x, lo, hi)
  REAL_VALUE_TYPE *x;
  unsigned HOST_WIDE_INT lo;
  unsigned HOST_WIDE_INT hi;
{
  REAL_VALUE_TYPE one, df, dg;

  saturn_real_clear (&one);
  one.mant = 1000000000000000ULL;
  df = saturn_real_ldexp (one, HOST_BITS_PER_WIDE_INT);
  dg = saturn_real_from_ulong (hi);
  dg = saturn_real_mul (dg, df);
  df = saturn_real_from_ulong (lo);
  dg = saturn_real_add (dg, df);
  saturn_real_copy (&dg, x);
}

REAL_VALUE_TYPE
saturn_real_from_float (f)
  HOST_WIDE_INT f;
{
  REAL_VALUE_TYPE r;

  /* UNFINISHED */

  saturn_real_clear (&r);
  return r;
}

REAL_VALUE_TYPE
saturn_real_from_double (d)
  HOST_WIDE_INT *d;
{
  REAL_VALUE_TYPE r;

  /* UNFINISHED */

  saturn_real_clear (&r);
  return r;
}

long
saturn_real_to_float (r)
  REAL_VALUE_TYPE r;
{
  /* UNFINISHED */

  return 0;
}

void
saturn_real_to_double (REAL_VALUE_TYPE r, long *d)
{
  /* UNFINISHED */

  d[0] = 0;
  d[1] = 0;
}

REAL_VALUE_TYPE
saturn_real_negate (x)
  REAL_VALUE_TYPE x;
{
  x.sign = -x.sign;
  return x;
}

REAL_VALUE_TYPE
saturn_real_truncate (mode, x)
  enum machine_mode mode;
  REAL_VALUE_TYPE x;
{
  struct saturn_ereal e;

  switch (mode)
    {
      case SFmode:
      case DFmode:
	e.sign = x.sign;
	e.mhi = x.mant;
	e.mlo = 0;
	e.esign = x.esign;
	e.exp = x.exp;
	return saturn_real_normalize (e, mode);
      default:
	return x;
    }
}

REAL_VALUE_TYPE
saturn_real_ldexp (x, n)
  REAL_VALUE_TYPE x;
  int n;
{
  REAL_VALUE_TYPE r;

  saturn_real_clear (&r);
  while (n)
    {
      if (n % 2)
	r = saturn_real_add (r, x);
      x = saturn_real_add (x, x);
      n /= 2;
    }
  return r;
}

int
saturn_real_cmp (x, y)
  REAL_VALUE_TYPE x;
  REAL_VALUE_TYPE y;
{
  int i;
  int msign;

  if (x.sign != y.sign)
    {
      if (x.mant != 0 || y.mant != 0)
        return x.sign;

      return 0;
    }

  /* Same sign. */
  if (x.sign == 0)
    msign = 1;
  else
    msign = -1;

  /* Compare the exponents. */
  if (x.esign > y.esign)
    return msign;
  else if (x.esign < x.esign)
    return -msign;
  else if (x.exp > y.exp)
    return msign;
  else if (x.exp < y.exp)
    return -msign;

  /* Compare the mantissa. */
  if (x.mant > y.mant)
    return msign;
  else if (x.mant < y.mant)
    return -msign;

  return 0;
}

#define INVALID_EXP	(-7)
#define chrtoi(c)	((int)((c) & 0x7f) - (int)'0')

REAL_VALUE_TYPE
saturn_real_atof (ss, mode)
  char *ss;
  enum machine_mode mode;
{
  REAL_VALUE_TYPE	 r;
  struct saturn_ereal	 er;
  long			 exp_exp = 0;
  long			 neg_exp;
  long			 e;
  int			 imp_exp = INVALID_EXP;
  int			 sign = 1;
  int			 exp_sign = 1;
  int			 lz_exp = 0;
  int			 ptr;
  int			 mptr = 0;
  int			 exparse = 0;
  int			 nonzero = 0;
  int			 i;
  char			*s;
  unsigned char		 mantissa[31];

  for (i = 0; i < 31; i++)
    mantissa[i] = 0;

  s = ss;
  while (*s == ' ')		/* skip leading spaces */
    ++s;

  ptr = -1;
  while (s[++ptr])
    {
      switch (s[ptr])
	{
	  case 'E':
	  case 'e':
	    if (exparse)
	      goto error;
	    exparse = 1;
	    if (imp_exp == INVALID_EXP)
	      imp_exp = ptr - 1;
	    break;

	  case '1':
	  case '2':
	  case '3':
	  case '4':
	  case '5':
	  case '6':
	  case '7':
	  case '8':
	  case '9':
	    nonzero = 1;
	  case '0':
	    if (exparse)
	      exp_exp = exp_exp * 10 + chrtoi (s[ptr]);
	    else if (s[ptr] != '0' || nonzero)
	      {
		if (mptr < 31)
		  mantissa[mptr++] = chrtoi (s[ptr]);
		else if (imp_exp == INVALID_EXP)
		  imp_exp = ptr - 1;
	      }
	    else
	      --lz_exp;
	    break;

	  case '-':
	    if (exparse)
	      exp_sign = -1;
	    else
	      {
		sign *= -1;
		++s;
		--ptr;
	      }
	    break;

	  case '+':
	    ++s;
	    --ptr;
	    break;

	  case '.':
	    imp_exp = ptr - 1;
	    break;

	  default:
	  error:
	    saturn_real_error ("real_atof", DOMAIN);
	    saturn_real_clear (&r);
	    return r;
	}
    }

  if (imp_exp == INVALID_EXP)
    imp_exp = ptr - 1;

  er.sign = sign;

  e = imp_exp + exp_sign * exp_exp + lz_exp;
  if (e < 0)
    { 
      er.esign = -1;
      er.exp = -e;
    }
  else
    {
      er.esign = 1;
      er.exp = e;
    }

  er.mhi = 0;
  for (i = 0; i < 15; i++)
    {
      er.mhi *= 10;
      er.mhi += mantissa[i];
    }
  er.mlo = 0;
  for (i = 0; i < 16; i++)
    {
      er.mlo *= 10;
      er.mlo += mantissa[15 + i];
    }

  return saturn_real_normalize (er, mode);
}

extern void print_real PROTO ((REAL_VALUE_TYPE));

void
print_real (r)
  REAL_VALUE_TYPE r;
{
  char *s, buf[100];
  int i;
  unsigned char data[15];

  s = buf;
  if (r.sign < 0)
    *s++ = '-';
  for (i = 0; i < 15; i++)
    {
      data[14 - i] = r.mant % 10;
      r.mant /= 10;
    }
  *s++ = data[0] + '0';
  *s++ = '.';
  for (i = 1; i < 15; i++)
    *s++ = data[i] + '0';
  *s++ = 'E';
  if (r.esign < 0)
    *s++ = '-';
  for (i = 0; i < 5; i++)
    {
      data[i] = r.exp % 10;
      r.exp /= 10;
    }
  for (i = 4; i >= 0; i--)
    *s++ = data[i] + '0';
  *s = '\0';
  fprintf (stderr, "%s\n", buf);
}

void
saturn_real_to_decimal (r, s)
  REAL_VALUE_TYPE r;
  char *s;
{
  int i;
  unsigned char data[15];

  if (r.sign < 0)
    *s++ = '-';
  for (i = 0; i < 15; i++)
    {
      data[14 - i] = r.mant % 10;
      r.mant /= 10;
    }
  *s++ = data[0] + '0';
  *s++ = '.';
  for (i = 1; i < 15; i++)
    *s++ = data[i] + '0';
  s--;
  while (*s == '0')
    s--;
  if (*s != '.')
    s++;
  if (r.exp > 0)
    {
      *s++ = 'E';
      if (r.esign < 0)
        *s++ = '-';
      for (i = 0; i < 5; i++)
        {
          data[i] = r.exp % 10;
          r.exp /= 10;
          if (r.exp == 0)
            break;
        }
      for ( ; i >= 0; i--)
        *s++ = data[i] + '0';
    }
  *s = '\0';
}

void
saturn_real_arith (value, icode, d1, d2)
  REAL_VALUE_TYPE *value;
  int icode;
  REAL_VALUE_TYPE *d1;
  REAL_VALUE_TYPE *d2;
{
  int code = (enum tree_code) icode;
  switch (code)
    {
      case PLUS_EXPR:	/* d1 + d2 */
	*value = saturn_real_add (*d1, *d2);
	break;
      case MINUS_EXPR:	/* d1 - d2 */
	*value = saturn_real_sub (*d1, *d2);
	break;
      case MULT_EXPR:	/* d1 * d2 */
	*value = saturn_real_mul (*d1, *d2);
	break;
      case RDIV_EXPR:	/* d1 / d2 */
	*value = saturn_real_div (*d1, *d2);
	break;
      case MIN_EXPR:	/* min (d1, d2) */
	if (saturn_real_cmp (*d1, *d2) < 0)
	  saturn_real_copy (d1, value);
	else
	  saturn_real_copy (d2, value);
	break;
      case MAX_EXPR:	/* max (d1, d2) */
	if (saturn_real_cmp (*d1, *d2) > 0)
	  saturn_real_copy (d1, value);
	else
	  saturn_real_copy (d2, value);
	break;
      default:
	saturn_real_clear (value);
	break;
    }
}

int
saturn_significand_size (mode)
  enum machine_mode mode;
{
  switch (mode)
    {
      case SFmode:
      case DFmode:
	return 39;

      case TFmode:
	return 49;

      default:
        abort ();
    }
}


void saturn_output_ascii(file, string, len)
     FILE *file;
     char *string;
     int   len;
{
    size_t n = len;
    char  *p = string;

    fputs("\ttextr \"", file);

    while(n--) {
	if(isprint(*p) && *p!='\\' && *p!='"')
	    fputc(*p, file);
	else
	    fprintf(file, "\\%03o", *p);
	++p;
    }

    fputs("\"\n", file);
}


syntax highlighted by Code2HTML, v. 0.9.1