Initial revision
authorRichard Kenner <kenner@gcc.gnu.org>
Fri, 9 Sep 1994 17:28:38 +0000 (13:28 -0400)
committerRichard Kenner <kenner@gcc.gnu.org>
Fri, 9 Sep 1994 17:28:38 +0000 (13:28 -0400)
From-SVN: r8049

gcc/config/dsp16xx/dsp16xx.c [new file with mode: 0644]

diff --git a/gcc/config/dsp16xx/dsp16xx.c b/gcc/config/dsp16xx/dsp16xx.c
new file mode 100644 (file)
index 0000000..08e8653
--- /dev/null
@@ -0,0 +1,2226 @@
+/* Subroutines for assembler code output on the DSP1610.
+   Copyright (C) 1994 Free Software Foundation, Inc.
+   Contributed by Michael Collison (collison@world.std.com).
+
+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 2, 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.  */
+
+/* Some output-actions in dsp1600.md need these.  */
+#include <stdio.h>
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "tree.h"
+#include "expr.h"
+#include "flags.h"
+
+char *text_seg_name;
+char *rsect_text;
+char *data_seg_name;
+char *rsect_data;
+char *bss_seg_name;
+char *rsect_bss;
+char *const_seg_name;
+char *rsect_const;
+
+char *chip_name;
+char *save_chip_name;
+
+/* Save the operands of a compare. The 16xx has not lt or gt, so
+   in these cases we swap the operands and reverse the condition */
+
+rtx dsp16xx_compare_op0;
+rtx dsp16xx_compare_op1;
+struct rtx_def *(*dsp16xx_compare_gen)();
+
+static char *fp;
+static char *sp;
+static char *rr;
+static char *a1h;
+
+struct dsp16xx_frame_info current_frame_info;
+struct dsp16xx_frame_info zero_frame_info;
+
+rtx dsp16xx_addhf3_libcall = (rtx) 0;
+rtx dsp16xx_subhf3_libcall = (rtx) 0;
+rtx dsp16xx_mulhf3_libcall = (rtx) 0;
+rtx dsp16xx_divhf3_libcall = (rtx) 0;
+rtx dsp16xx_cmphf3_libcall = (rtx) 0;
+rtx dsp16xx_fixhfhi2_libcall = (rtx) 0;
+rtx dsp16xx_floathihf2_libcall = (rtx) 0;
+rtx dsp16xx_neghf2_libcall = (rtx) 0;
+
+rtx dsp16xx_mulhi3_libcall = (rtx) 0;
+rtx dsp16xx_udivqi3_libcall = (rtx) 0;
+rtx dsp16xx_udivhi3_libcall = (rtx) 0;
+rtx dsp16xx_divqi3_libcall = (rtx) 0;
+rtx dsp16xx_divhi3_libcall = (rtx) 0;
+rtx dsp16xx_modqi3_libcall = (rtx) 0;
+rtx dsp16xx_modhi3_libcall = (rtx) 0;
+rtx dsp16xx_umodqi3_libcall = (rtx) 0;
+rtx dsp16xx_umodhi3_libcall = (rtx) 0;
+rtx dsp16xx_ashrhi3_libcall = (rtx) 0;
+rtx dsp16xx_ashlhi3_libcall = (rtx) 0;
+rtx dsp16xx_ucmphi2_libcall = (rtx) 0;
+rtx dsp16xx_lshrhi3_libcall = (rtx) 0;
+
+char *himode_reg_name[] = HIMODE_REGISTER_NAMES;
+
+#define SHIFT_INDEX_1   0
+#define SHIFT_INDEX_4   1
+#define SHIFT_INDEX_8   2
+#define SHIFT_INDEX_16  3
+
+static char *ashift_right_asm[] = 
+{
+  "%0=%0>>1",
+  "%0=%0>>4",
+  "%0=%0>>8",
+  "%0=%0>>16"
+};
+
+static char *ashift_right_asm_first[] = 
+{
+  "%0=%1>>1",
+  "%0=%1>>4",
+  "%0=%1>>8",
+  "%0=%1>>16"
+};
+
+static char *ashift_left_asm[] = 
+{
+  "%0=%0<<1",
+  "%0=%0<<4",
+  "%0=%0<<8",
+  "%0=%0<<16"
+};
+
+static char *ashift_left_asm_first[] = 
+{
+  "%0=%1<<1",
+  "%0=%1<<4",
+  "%0=%1<<8",
+  "%0=%1<<16"
+};
+
+static char *lshift_right_asm[] = 
+{
+  "%0=%0>>1\n\t%0=%b0&0x7fff",
+  "%0=%0>>4\n\t%0=%b0&0x0fff",
+  "%0=%0>>8\n\t%0=%b0&0x00ff",
+  "%0=%0>>16\n\t%0=%b0&0x0000"
+};
+
+static char *lshift_right_asm_first[] = 
+{
+  "%0=%1>>1\n\t%0=%b0&0x7fff",
+  "%0=%1>>4\n\t%0=%b0&0x0fff",
+  "%0=%1>>8\n\t%0=%b0&0x00ff",
+  "%0=%1>>16\n\t%0=%b0&0x0000"
+};
+
+int 
+hard_regno_mode_ok (regno, mode)
+int regno;
+enum machine_mode mode;
+{
+  switch ((int) mode)
+    {
+    case VOIDmode:
+      return 1;
+      
+      /* 
+       We can't use the c0-c2 for QImode, since they are only
+       8 bits in length */
+
+    case QImode:
+      if (regno != REG_C0 && regno != REG_C1 && regno != REG_C2)
+       return 1;
+      else
+       return 0;
+      
+      /* We only allow a0, a1, y, and p to be allocated for 32-bit modes.
+         Additionally we allow the virtual ybase registers to be used for 32-bit
+        modes. */
+      
+    case HFmode:
+    case SFmode:
+    case DFmode:
+    case XFmode:
+    case HImode:
+    case SImode:
+    case DImode:
+      if (regno == REG_A0 || regno == REG_A1 || regno == REG_Y || regno == REG_PROD
+         || (IS_YBASE_REGISTER_WINDOW(regno) && ((regno & 1) == 0)))
+       return 1;
+      else
+       return 0;
+      
+    default:
+      return 0;
+    }
+}
+
+enum reg_class
+dsp16xx_reg_class_from_letter (c)
+int c;
+{
+  switch (c)
+    {
+    case 'A':
+      return ACCUM_REGS;
+      
+    case 'h':
+      return ACCUM_HIGH_REGS;
+      
+    case 'j':
+      return A0H_REG;
+      
+    case 'k':
+      return A0L_REG;
+      
+    case 'q':
+      return A1H_REG;
+      
+    case 'u':
+      return A1L_REG;
+      
+    case 'x':
+      return X_REG;
+
+    case 'y':
+      return YH_REG;
+
+    case 'z':
+      return YL_REG;
+
+    case 't':
+      return P_REG;
+
+    case 'Z':
+      return Y_OR_P_REGS;
+
+    case 'd':
+      return ACCUM_Y_OR_P_REGS;
+
+    case 'C':
+      return NO_FRAME_Y_ADDR_REGS;
+
+    case 'a':
+      return Y_ADDR_REGS;
+
+    case 'B':
+      return (TARGET_BMU ? BMU_REGS : NO_REGS);
+
+    case 'Y':
+      return YBASE_VIRT_REGS;
+
+    case 'v':
+      return PH_REG;
+
+    case 'w':
+      return PL_REG;
+
+    case 'W':
+      return J_REG;
+
+    case 'e':
+      return YBASE_ELIGIBLE_REGS;
+
+    case 'b':
+      return ACCUM_LOW_REGS;
+
+    case 'c':
+      return NON_YBASE_REGS;
+
+    case 'f':
+      return Y_REG;
+
+    case 'D':
+      return SLOW_MEM_LOAD_REGS;
+
+    default:
+      fatal ("Illegal register class letter %c", c);
+      return NO_REGS;
+    }
+}
+/* Return the class number of the smallest class containing
+   reg number REGNO. */
+
+int 
+regno_reg_class(regno)
+int regno;
+{
+  switch (regno)
+    {
+    case REG_A0L:
+      return (int) A0L_REG;
+    case REG_A1L:
+      return (int) A1L_REG;
+      
+    case REG_A0:
+      return (int) A0H_REG;
+    case REG_A1:
+      return (int) A1H_REG;
+      
+    case REG_X:
+      return (int) X_REG;
+      
+    case REG_Y:
+      return (int) YH_REG;
+    case REG_YL:
+      return (int) YL_REG;
+      
+    case REG_PROD:
+      return (int) PH_REG;
+    case REG_PRODL:
+      return (int) PL_REG;
+      
+    case REG_R0: case REG_R1: case REG_R2: case REG_R3:
+      return (int) Y_ADDR_REGS;
+      
+    case REG_J:
+      return (int) J_REG;
+    case REG_K:
+      return (int) GENERAL_REGS;
+      
+    case REG_YBASE:
+      return (int) GENERAL_REGS;
+      
+    case REG_PT:
+      return (int) GENERAL_REGS;
+      
+    case REG_AR0: case REG_AR1: case REG_AR2: case REG_AR3:
+      return (int) BMU_REGS;
+      
+    case REG_C0: case REG_C1: case REG_C2:
+      return (int) GENERAL_REGS;
+      
+    case REG_PR:
+      return (int) GENERAL_REGS;
+      
+    case REG_RB:
+      return (int) GENERAL_REGS;
+      
+    case REG_YBASE0: case REG_YBASE1: case REG_YBASE2: case REG_YBASE3:
+    case REG_YBASE4: case REG_YBASE5: case REG_YBASE6: case REG_YBASE7:
+    case REG_YBASE8: case REG_YBASE9: case REG_YBASE10: case REG_YBASE11:
+    case REG_YBASE12: case REG_YBASE13: case REG_YBASE14: case REG_YBASE15:
+    case REG_YBASE16: case REG_YBASE17: case REG_YBASE18: case REG_YBASE19:
+    case REG_YBASE20: case REG_YBASE21: case REG_YBASE22: case REG_YBASE23:
+    case REG_YBASE24: case REG_YBASE25: case REG_YBASE26: case REG_YBASE27:
+    case REG_YBASE28: case REG_YBASE29: case REG_YBASE30: case REG_YBASE31:
+      return (int) YBASE_VIRT_REGS;
+      
+    default:
+      return (int) NO_REGS;
+    }
+}
+
+/* A C expression for the maximum number of consecutive registers of class CLASS
+   needed to hold a value of mode MODE */
+
+int
+class_max_nregs(class, mode)
+enum reg_class class;
+enum machine_mode mode;
+{
+    return (GET_MODE_SIZE(mode));
+}
+
+enum reg_class
+limit_reload_class (mode, class)
+enum machine_mode mode;
+enum reg_class class;
+{
+  switch ((int) class)
+    {
+    case NO_REGS:
+    case A0H_REG:
+    case A0L_REG:
+    case A0_REG:
+    case A1H_REG:
+      return class;
+
+    case ACCUM_HIGH_REGS:
+      fatal ("ACCUM_HIGH_REGS class in limit_reload_class");
+
+    case A1L_REG:
+    case ACCUM_LOW_REGS:
+    case A1_REG:
+      return class;
+
+    case ACCUM_REGS:
+      if (GET_MODE_SIZE(mode) == 1)
+       return ACCUM_LOW_REGS;
+      else
+       return class;
+
+    case X_REG:
+    case X_OR_ACCUM_LOW_REGS:
+      return class;
+
+    case X_OR_ACCUM_REGS:
+      if (GET_MODE_SIZE(mode) == 1)
+       return X_OR_ACCUM_LOW_REGS;
+      else
+       return class;
+
+    case YH_REG:
+      return class;
+
+    case YH_OR_ACCUM_HIGH_REGS:
+      fatal ("YH_OR_ACCUM_HIGH_REGS found in limit_reload_class");
+
+    case X_OR_YH_REGS:
+      return class;
+
+    case YL_REG:
+      /* Register 'yl' is illegal for QImode, so we should never
+        see it. */
+
+      fatal ("YL found in limit_reload_class");
+
+    case YL_OR_ACCUM_LOW_REGS:
+    case X_OR_YL_REGS:
+      return class;
+
+    case Y_REG:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return YH_REG;
+
+    case ACCUM_OR_Y_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return YL_OR_ACCUM_LOW_REGS;
+
+    case PH_REG:
+    case X_OR_PH_REGS:
+    case PL_REG:
+    case PL_OR_ACCUM_LOW_REGS:
+    case X_OR_PL_REGS:
+      return class;
+
+    case P_REG:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return PL_REG;
+
+    case ACCUM_OR_P_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return PL_OR_ACCUM_LOW_REGS;
+
+    case YL_OR_P_REGS:
+    case ACCUM_LOW_OR_YL_OR_P_REGS:
+      return class;
+
+    case Y_OR_P_REGS:
+      return class;
+
+    case ACCUM_Y_OR_P_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return ACCUM_LOW_OR_YL_OR_P_REGS;
+
+    case NO_FRAME_Y_ADDR_REGS:
+    case Y_ADDR_REGS: 
+    case ACCUM_LOW_OR_Y_ADDR_REGS:
+      return class;
+
+    case ACCUM_OR_Y_ADDR_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return ACCUM_REGS;
+      else
+       return ACCUM_LOW_OR_Y_ADDR_REGS;
+
+    case X_OR_Y_ADDR_REGS:
+      return class;
+
+    case Y_OR_Y_ADDR_REGS:
+    case P_OR_Y_ADDR_REGS:
+    case NON_HIGH_YBASE_ELIGIBLE_REGS:
+
+    case J_REG:
+      return class;
+
+    case YBASE_ELIGIBLE_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return ACCUM_Y_P_OR_YBASE_REGS;
+      else
+       return NON_HIGH_YBASE_ELIGIBLE_REGS;
+
+    case J_OR_DAU_16_BIT_REGS:
+      if (GET_MODE_SIZE(mode) == 1)
+       return J_REG;
+      else
+       return class;
+
+    case BMU_REGS:
+    case NOHIGH_NON_ADDR_REGS:
+      return class;
+
+    case NON_ADDR_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return NOHIGH_NON_ADDR_REGS;
+
+    case NOHIGH_NON_YBASE_REGS:
+      return class;
+
+    case NON_YBASE_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return NOHIGH_NON_YBASE_REGS;
+
+    case YBASE_VIRT_REGS:
+    case ACCUM_LOW_OR_YBASE_REGS:
+      return class;
+      
+    case ACCUM_OR_YBASE_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return ACCUM_LOW_OR_YBASE_REGS;
+
+    case X_OR_YBASE_REGS:
+      return class;
+
+    case Y_OR_YBASE_REGS:
+    case ACCUM_LOW_YL_PL_OR_YBASE_REGS:
+    case P_OR_YBASE_REGS:
+      return class;
+
+    case ACCUM_Y_P_OR_YBASE_REGS:
+      return ACCUM_LOW_YL_PL_OR_YBASE_REGS;
+
+    case Y_ADDR_OR_YBASE_REGS:
+    case YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS:
+      return class;
+
+    case YBASE_OR_YBASE_ELIGIBLE_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return YBASE_OR_NOHIGH_YBASE_ELIGIBLE_REGS;
+
+    case NO_HIGH_ALL_REGS:
+      return class;
+
+    case ALL_REGS:
+      if (GET_MODE_SIZE(mode) > 1)
+       return class;
+      else
+       return NO_HIGH_ALL_REGS;
+
+    default:
+      return class;
+    }
+}
+
+int
+dsp16xx_register_move_cost (from, to)
+enum reg_class from, to;
+{
+#if 0
+  if (from == NO_REGS || to == NO_REGS || (from == to))
+    return 2;
+#endif
+
+  if (from == A0H_REG || from == A0L_REG || from == A0_REG ||
+      from == A1H_REG || from == ACCUM_HIGH_REGS || from == A1L_REG ||
+      from == ACCUM_LOW_REGS || from == A1_REG || from == ACCUM_REGS)
+    {
+      if (to == Y_REG || to == P_REG)
+       return 4;
+      else
+       return 2;
+    }
+
+  if (to == A0H_REG || to == A0L_REG || to == A0_REG ||
+      to == A1H_REG || to == ACCUM_HIGH_REGS || to == A1L_REG ||
+      to == ACCUM_LOW_REGS || to == A1_REG || to == ACCUM_REGS)
+    {
+      return 2;
+    }
+
+#if 0
+  if (from == YBASE_VIRT_REGS)
+    {
+      if (to == X_REG || to == YH_REG || to == YL_REG ||
+         to == Y_REG || to == PL_REG || to == PH_REG ||
+         to == P_REG || to == Y_ADDR_REGS || to == YBASE_ELIGIBLE_REGS ||
+         to == Y_OR_P_REGS)
+       {
+         return 2;
+       }
+      else
+       return 4;
+    }
+
+  if (to == YBASE_VIRT_REGS)
+    {
+      if (from == X_REG || from == YH_REG || from == YL_REG ||
+         from == Y_REG || from == PL_REG || from == PH_REG ||
+         from == P_REG || from == Y_ADDR_REGS || from == YBASE_ELIGIBLE_REGS ||
+         from == Y_OR_P_REGS)
+       {
+         return 2;
+       }
+      else
+       return 4;
+    }
+#endif
+  return 4;
+}
+
+/* Given an rtx X being reloaded into a reg required to be
+   in class CLASS, return the class of reg to actually use.
+   In general this is just CLASS; but on some machines
+   in some cases it is preferable to use a more restrictive class.
+   Also, we must ensure that a PLUS is reloaded either
+   into an accumulator or an address register.  */
+
+enum reg_class
+preferred_reload_class (x, class)
+     rtx x;
+     enum reg_class class;
+{
+  /* The ybase registers cannot have constants copied directly
+     to them. */
+
+  if (CONSTANT_P (x))
+    {
+      if (class == ALL_REGS)
+       return NON_YBASE_REGS;
+    }
+
+  if (class == ALL_REGS && REG_P (x) && !TARGET_RESERVE_YBASE
+      && IS_YBASE_REGISTER_WINDOW (REGNO(x)))
+    return YBASE_ELIGIBLE_REGS;
+
+  if (GET_CODE (x) == PLUS)
+    {
+      if (GET_MODE (x) == QImode
+         && REG_P (XEXP (x,0))
+         && (XEXP (x,0) == frame_pointer_rtx
+             || XEXP (x,0) == stack_pointer_rtx)
+         && (GET_CODE (XEXP (x,1)) == CONST_INT))
+       {
+         if (class == ACCUM_HIGH_REGS)
+           return class;
+
+         if (reg_class_subset_p (ACCUM_HIGH_REGS, class))
+           return ACCUM_HIGH_REGS;
+
+         /* We will use accumulator 'a1l' for reloading a
+            PLUS. We can only use one accumulator because
+            'reload_inqi' only allows one alternative to be
+            used. */
+
+         else if (class == ACCUM_LOW_REGS)
+           return A1L_REG;
+         else if (class == A0L_REG)
+           return NO_REGS;
+         else
+           return class;
+       }
+
+      if (class == NON_YBASE_REGS || class == YBASE_ELIGIBLE_REGS)
+       return Y_ADDR_REGS;
+      else
+       return class;
+    }
+  else if (GET_CODE (x) == MEM)
+    {
+      if (class == ALL_REGS)
+       {
+#if 0
+         if (GET_MODE(x) == HImode)
+           return NO_ACCUM_NON_YBASE_REGS;
+         else
+#endif
+           return NON_YBASE_REGS;
+       }
+      else
+       return class;
+    }
+  else
+    return class;
+}
+       
+/* Return the register class of a scratch register needed to copy IN into
+   or out of a register in CLASS in MODE.  If it can be done directly,
+   NO_REGS is returned.  */
+
+enum reg_class
+secondary_reload_class (class, mode, in)
+     enum reg_class class;
+     enum machine_mode mode;
+     rtx in;
+{
+  int regno = -1;
+
+  if (GET_CODE (in) == REG || GET_CODE (in) == SUBREG)
+    regno = true_regnum (in);
+
+  if (class == ACCUM_HIGH_REGS 
+      || class == ACCUM_LOW_REGS
+      || class == A1L_REG
+      || class == A0L_REG
+      || class == A1H_REG
+      || class == A0H_REG)
+    {
+      if (GET_CODE (in) == PLUS && mode == QImode)
+       {
+         rtx addr0 = XEXP (in, 0);
+         rtx addr1 = XEXP (in, 1);
+         
+         /* If we are reloading a plus (reg:QI) (reg:QI)
+            we need an additional register. */ 
+         if (REG_P (addr0) && REG_P (addr1))
+           return NO_REGS;
+       }
+    }
+
+  /* We can place anything into ACCUM_REGS and can put ACCUM_REGS
+     into anything.  */
+
+  if ((class == ACCUM_REGS || class == ACCUM_HIGH_REGS ||
+       class == ACCUM_LOW_REGS || class == A0H_REG || class == A0L_REG ||
+       class == A1H_REG || class == A1_REG) || 
+      (regno >= REG_A0 && regno < REG_A1L + 1))
+    return NO_REGS;
+
+  /* We can copy the ybase registers into:
+     r0-r3, a0-a1, y, p, & x or the union of
+     any of these. */
+
+  if (!TARGET_RESERVE_YBASE && IS_YBASE_REGISTER_WINDOW(regno))
+    {
+      switch ((int) class)
+       {
+       case (int) X_REG:
+       case (int) X_OR_ACCUM_LOW_REGS:
+       case (int) X_OR_ACCUM_REGS:
+       case (int) YH_REG:
+       case (int) YH_OR_ACCUM_HIGH_REGS:
+       case (int) X_OR_YH_REGS:
+       case (int) YL_REG:
+       case (int) YL_OR_ACCUM_LOW_REGS:
+       case (int) X_OR_Y_REGS:
+       case (int) X_OR_YL_REGS:
+       case (int) Y_REG:
+       case (int) ACCUM_OR_Y_REGS:
+       case (int) PH_REG:
+       case (int) X_OR_PH_REGS:
+       case (int) PL_REG:
+       case (int) PL_OR_ACCUM_LOW_REGS:
+       case (int) X_OR_PL_REGS:
+       case (int) YL_OR_PL_OR_ACCUM_LOW_REGS:
+       case (int) P_REG:
+       case (int) ACCUM_OR_P_REGS:
+       case (int) YL_OR_P_REGS:
+       case (int) ACCUM_LOW_OR_YL_OR_P_REGS:
+       case (int) Y_OR_P_REGS:
+       case (int) ACCUM_Y_OR_P_REGS:
+       case (int) Y_ADDR_REGS:
+       case (int) ACCUM_LOW_OR_Y_ADDR_REGS:
+       case (int) ACCUM_OR_Y_ADDR_REGS:
+       case (int) X_OR_Y_ADDR_REGS:
+       case (int) Y_OR_Y_ADDR_REGS:
+       case (int) P_OR_Y_ADDR_REGS:
+       case (int) YBASE_ELIGIBLE_REGS:
+         return NO_REGS;
+
+       default:
+         return ACCUM_HIGH_REGS;
+       }
+    }
+
+  /* We can copy r0-r3, a0-a1, y, & p
+     directly to the ybase registers. In addition
+     we can use any of the ybase virtual registers
+     as the secondary reload registers when copying
+     between any of these registers. */
+
+  if (!TARGET_RESERVE_YBASE && regno != -1)
+    {
+      switch (regno)
+       {
+       case REG_A0:
+       case REG_A0L:
+       case REG_A1:
+       case REG_A1L:
+       case REG_X:
+       case REG_Y:
+       case REG_YL:
+       case REG_PROD:
+       case REG_PRODL:
+       case REG_R0:
+       case REG_R1:
+       case REG_R2:
+       case REG_R3:
+         if (class == YBASE_VIRT_REGS)
+           return NO_REGS;
+         else
+           {
+             switch ((int) class)
+               {
+               case (int) X_REG:
+               case (int) X_OR_ACCUM_LOW_REGS:
+               case (int) X_OR_ACCUM_REGS:
+               case (int) YH_REG:
+               case (int) YH_OR_ACCUM_HIGH_REGS:
+               case (int) X_OR_YH_REGS:
+               case (int) YL_REG:
+               case (int) YL_OR_ACCUM_LOW_REGS:
+               case (int) X_OR_Y_REGS:
+               case (int) X_OR_YL_REGS:
+               case (int) Y_REG:
+               case (int) ACCUM_OR_Y_REGS:
+               case (int) PH_REG:
+               case (int) X_OR_PH_REGS:
+               case (int) PL_REG:
+               case (int) PL_OR_ACCUM_LOW_REGS:
+               case (int) X_OR_PL_REGS:
+               case (int) YL_OR_PL_OR_ACCUM_LOW_REGS:
+               case (int) P_REG:
+               case (int) ACCUM_OR_P_REGS:
+               case (int) YL_OR_P_REGS:
+               case (int) ACCUM_LOW_OR_YL_OR_P_REGS:
+               case (int) Y_OR_P_REGS:
+               case (int) ACCUM_Y_OR_P_REGS:
+               case (int) Y_ADDR_REGS:
+               case (int) ACCUM_LOW_OR_Y_ADDR_REGS:
+               case (int) ACCUM_OR_Y_ADDR_REGS:
+               case (int) X_OR_Y_ADDR_REGS:
+               case (int) Y_OR_Y_ADDR_REGS:
+               case (int) P_OR_Y_ADDR_REGS:
+               case (int) YBASE_ELIGIBLE_REGS:
+                 return YBASE_VIRT_REGS;
+
+               default:
+                 break;
+               }
+           }
+       }
+    }
+
+  /* Memory or constants can be moved from or to any register
+     except the ybase virtual registers */
+  if (regno == -1 && GET_CODE(in) != PLUS)
+    {
+      if (class == YBASE_VIRT_REGS)
+       return NON_YBASE_REGS;
+      else
+        return NO_REGS;
+    }
+
+  if (GET_CODE (in) == PLUS && mode == QImode)
+    {
+      rtx addr0 = XEXP (in, 0);
+      rtx addr1 = XEXP (in, 1);
+
+      /* If we are reloading a plus (reg:QI) (reg:QI)
+        we need a low accumulator, not a high one. */
+      if (REG_P (addr0) && REG_P (addr1))
+       return ACCUM_LOW_REGS;
+    }
+
+#if 0
+  if (REG_P(in))
+    return ACCUM_REGS;
+#endif
+
+  /* Otherwise, we need a high accumulator(s).  */
+  return ACCUM_HIGH_REGS;
+}
+
+int
+symbolic_address_operand (op, mode)
+rtx op;
+enum machine_mode mode;
+{
+    return (symbolic_address_p (op));
+
+}
+
+int symbolic_address_p (op)
+rtx op;
+{
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return 1;
+
+    case CONST:
+      op = XEXP (op, 0);
+      return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+              || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+             && GET_CODE (XEXP (op, 1)) == CONST_INT
+              && INTVAL (XEXP (op,1)) < 0x20);
+
+    default:
+      return 0;
+    }
+}
+
+/* For a Y address space operand we allow only *rn, *rn++, *rn--.
+   This routine only recognizes *rn, the '<>' constraints recognize
+   *rn++, *rn-- */
+
+int
+Y_address_operand (op, mode)
+rtx op;
+enum machine_mode mode;
+{
+   return (memory_address_p (mode, op) && !symbolic_address_p (op));
+}           
+
+int
+sp_operand (op, mode)
+rtx op;
+enum machine_mode mode;
+{
+    return (GET_CODE (op) == PLUS
+           && (XEXP (op, 0) == stack_pointer_rtx
+               || XEXP (op, 0) == frame_pointer_rtx)
+           && GET_CODE (XEXP (op,1)) == CONST_INT);
+}
+
+int
+sp_operand2 (op, mode)
+rtx op;
+enum machine_mode mode;
+{
+  if ((GET_CODE (op) == PLUS 
+       && (XEXP (op, 0) == stack_pointer_rtx
+          || XEXP (op, 0) == frame_pointer_rtx)
+       && (REG_P (XEXP (op,1))
+          && IS_ADDRESS_REGISTER (REGNO (XEXP(op, 1))))))
+    return 1;
+  else if ((GET_CODE (op) == PLUS
+       && (XEXP (op, 1) == stack_pointer_rtx
+          || XEXP (op, 1) == frame_pointer_rtx)
+       && (REG_P (XEXP (op,0))
+          && IS_ADDRESS_REGISTER (REGNO (XEXP(op, 1))))))
+    return 1;
+  else
+    return 0;
+}
+
+int
+nonmemory_arith_operand (op, mode)
+rtx op;
+enum machine_mode mode;
+{
+  return (immediate_operand (op, mode) || arith_reg_operand (op, mode));
+}
+
+int
+arith_reg_operand (op, mode)
+rtx op;
+enum machine_mode mode;
+{
+  return (register_operand (op, mode)
+         && (GET_CODE (op) != REG
+             || REGNO (op) >= FIRST_PSEUDO_REGISTER
+             || (!(IS_YBASE_REGISTER_WINDOW (REGNO (op)))
+                 && REGNO (op) != FRAME_POINTER_REGNUM)));
+}
+
+int
+call_address_operand (op, mode)
+rtx op;
+enum machine_mode mode;
+{
+    if (symbolic_address_p (op) || REG_P(op))
+    {
+       return 1;
+    }
+
+    return 0;
+}
+
+int
+dsp16xx_comparison_operator (op, mode)
+    register rtx op;
+    enum machine_mode mode;
+{
+  return ((mode == VOIDmode || GET_MODE (op) == mode)
+         && GET_RTX_CLASS (GET_CODE (op)) == '<'
+         && (GET_CODE(op) != GE && GET_CODE (op) != LT &&
+             GET_CODE (op) != GEU && GET_CODE (op) != LTU));
+}
+
+void
+notice_update_cc(exp)
+rtx exp;
+{
+    if (GET_CODE (exp) == SET)
+    {
+       /* Jumps do not alter the cc's.  */
+
+       if (SET_DEST (exp) == pc_rtx)
+           return;
+
+       /* Moving register or memory into a register:
+          it doesn't alter the cc's, but it might invalidate
+          the RTX's which we remember the cc's came from.
+          (Note that moving a constant 0 or 1 MAY set the cc's).  */
+       if (REG_P (SET_DEST (exp))
+           && (REG_P (SET_SRC (exp)) || GET_CODE (SET_SRC (exp)) == MEM))
+       {
+           if (cc_status.value1
+               && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value1))
+               cc_status.value1 = 0;
+           if (cc_status.value2
+               && reg_overlap_mentioned_p (SET_DEST (exp), cc_status.value2))
+               cc_status.value2 = 0;
+           return;
+       }
+       /* Moving register into memory doesn't alter the cc's.
+          It may invalidate the RTX's which we remember the cc's came from.  */
+       if (GET_CODE (SET_DEST (exp)) == MEM && REG_P (SET_SRC (exp)))
+       {
+           if (cc_status.value1 && GET_CODE (cc_status.value1) == MEM)
+               cc_status.value1 = 0;
+           if (cc_status.value2 && GET_CODE (cc_status.value2) == MEM)
+               cc_status.value2 = 0;
+           return;
+       }
+       /* Function calls clobber the cc's.  */
+       else if (GET_CODE (SET_SRC (exp)) == CALL)
+       {
+           CC_STATUS_INIT;
+           return;
+       }
+       /* Tests and compares set the cc's in predictable ways.  */
+       else if (SET_DEST (exp) == cc0_rtx)
+       {
+           CC_STATUS_INIT;
+           cc_status.value1 = SET_SRC (exp);
+           return;
+       }
+       /* Certain instructions effect the condition codes. */
+       else if (GET_MODE_CLASS (GET_MODE (SET_SRC (exp))) == MODE_INT)
+           switch( GET_CODE (SET_SRC (exp)) )
+           {
+           case PLUS: 
+           case MINUS:
+             if (REG_P (SET_DEST (exp)))
+               {
+                 /* Address registers don't set the condition codes */
+                 if (IS_ADDRESS_REGISTER (REGNO (SET_DEST (exp))))
+                   {
+                     CC_STATUS_INIT;
+                     break;
+                   }
+               }
+           case ASHIFTRT: 
+           case LSHIFTRT:
+           case ASHIFT: 
+           case LSHIFT:
+           case AND: 
+           case IOR: 
+           case XOR:
+           case MULT:
+           case NEG:
+           case NOT:
+             cc_status.value1 = SET_SRC (exp);
+             cc_status.value2 = SET_DEST (exp);
+             break;
+             
+           default:
+             CC_STATUS_INIT;
+           }
+       else
+       {
+           CC_STATUS_INIT;
+       }
+    }
+    else if (GET_CODE (exp) == PARALLEL
+            && GET_CODE (XVECEXP (exp, 0, 0)) == SET)
+    {
+       if (SET_DEST (XVECEXP (exp, 0, 0)) == pc_rtx)
+           return;
+
+       if (SET_DEST (XVECEXP (exp, 0, 0)) == cc0_rtx)
+       {
+           CC_STATUS_INIT;
+           cc_status.value1 = SET_SRC (XVECEXP (exp, 0, 0));
+           return;
+       }
+
+       CC_STATUS_INIT;
+    }
+    else
+    {
+       CC_STATUS_INIT;
+    }
+}
+
+int
+dsp16xx_makes_calls ()
+{
+  rtx insn;
+
+  for (insn = get_insns (); insn; insn = next_insn (insn))
+    if (GET_CODE (insn) == CALL_INSN)
+      return (1);
+
+  return 0;
+}
+
+long compute_frame_size (size)
+int size;
+{
+  long total_size;
+  long var_size;
+  long args_size;
+  long extra_size;
+  long reg_size;
+
+  reg_size = 0;
+  extra_size = 0;
+  var_size = size;
+  args_size = current_function_outgoing_args_size;
+  reg_size = reg_save_size ();  
+
+  total_size = var_size + args_size + extra_size + reg_size;
+
+
+  /* Save other computed information.  */
+  current_frame_info.total_size  = total_size;
+  current_frame_info.var_size    = var_size;
+  current_frame_info.args_size   = args_size;
+  current_frame_info.extra_size  = extra_size;
+  current_frame_info.reg_size    = reg_size;
+  current_frame_info.initialized = reload_completed;
+  current_frame_info.reg_size   = reg_size / UNITS_PER_WORD;
+  current_frame_info.function_makes_calls = dsp16xx_makes_calls ();
+
+  if (reg_size)
+    {
+      unsigned long offset = args_size + var_size + reg_size;
+      current_frame_info.sp_save_offset = offset;
+      current_frame_info.fp_save_offset = offset - total_size;
+    }
+
+  return total_size;
+}
+
+int
+dsp16xx_call_saved_register (regno)
+int regno;
+{
+  return (regs_ever_live[regno] && !call_used_regs[regno] &&
+         !IS_YBASE_REGISTER_WINDOW(regno));
+
+}
+
+int
+ybase_regs_ever_used ()
+{
+  int regno;
+  int live = 0;
+
+  for (regno = REG_YBASE0; regno <= REG_YBASE31; regno++)
+    if (regs_ever_live[regno])
+      {
+       live = 1;
+       break;
+      }
+
+  return live;
+}
+
+void 
+function_prologue (file, size)
+FILE *file;
+int  size;
+{
+  int regno;
+  long total_size;
+  fp = reg_names[FRAME_POINTER_REGNUM];
+  sp = reg_names[STACK_POINTER_REGNUM];
+  rr = reg_names[RETURN_ADDRESS_REGNUM];   /* return address register */
+  a1h = reg_names[REG_A1];
+  
+  total_size = compute_frame_size (size);
+  
+  fprintf( file, "\t/* FUNCTION PROLOGUE: */\n" );
+  fprintf (file, "\t/* total=%d, vars= %d, regs= %d, args=%d, extra= %d */\n",
+          current_frame_info.total_size,
+          current_frame_info.var_size,
+          current_frame_info.reg_size,
+          current_function_outgoing_args_size,
+          current_frame_info.extra_size);
+  
+  fprintf (file, "\t/* fp save offset= %d, sp save_offset= %d */\n\n",
+          current_frame_info.fp_save_offset,
+          current_frame_info.sp_save_offset);
+  /* Set up the 'ybase' register window. */
+  
+  if (ybase_regs_ever_used())
+    {
+      fprintf (file, "\t%s=%s\n", a1h, reg_names[REG_YBASE]);
+      if (TARGET_YBASE_HIGH)
+       fprintf (file, "\t%s=%sh-32\n", reg_names[REG_A1], a1h);
+      else
+       fprintf (file, "\t%s=%sh+32\n", reg_names[REG_A1], a1h);
+      fprintf (file, "\t%s=%s\n", reg_names[REG_YBASE], a1h);
+    }
+  
+#if 0
+  if (current_frame_info.function_makes_calls)
+    fprintf( file, "\t*%s++=%s\n", sp, rr );   /* Push return address */
+#endif
+  
+  
+  if (current_frame_info.var_size)
+    {
+      if (current_frame_info.var_size == 1)
+       fprintf (file, "\t*%s++\n", sp);
+      else
+        {
+         if(SMALL_INTVAL(current_frame_info.var_size) && ((current_frame_info.var_size & 0x8000) == 0))
+           fprintf (file, "\t%s=%d\n\t*%s++%s\n", reg_names[REG_J], current_frame_info.var_size, sp, reg_names[REG_J]);
+         else
+           fatal ("Stack size > 32k");
+       }
+    }
+  
+  /* Save any registers this function uses, unless they are
+   * used in a call, in which case we don't need to
+   */
+  
+  for( regno = 0; regno < FIRST_PSEUDO_REGISTER; ++ regno )
+    if (dsp16xx_call_saved_register (regno)) 
+      {
+#if OLD_REGISTER_SAVE
+       fprintf( file, "\t*%s++=%s\n", sp, reg_names[regno] );
+#else
+       fprintf( file, "\tpush(*%s)=%s\n", sp, reg_names[regno] );
+#endif
+      }
+  
+  if (current_frame_info.args_size)
+    {
+      if (current_frame_info.args_size == 1)
+       fprintf (file, "\t*%s++\n", sp);
+      else
+        {
+         if(SMALL_INTVAL(current_frame_info.args_size) && ((current_frame_info.args_size & 0x8000) == 0))
+           fprintf (file, "\t%s=%d\n\t*%s++%s\n", reg_names[REG_J], current_frame_info.args_size, sp, reg_names[REG_J]);
+         else
+           fatal ("Stack size > 32k");
+       }
+    }
+  
+  if (frame_pointer_needed)
+    {
+      fprintf( file, "\t%s=%s\n", a1h, sp );
+      fprintf( file, "\t%s=%s\n", fp, a1h );  /* Establish new base frame */
+      fprintf( file, "\t%s=%d\n", reg_names[REG_J], -total_size);
+      fprintf( file, "\t*%s++%s\n", fp, reg_names[REG_J]);
+    }
+  
+  fprintf( file, "\t/* END FUNCTION PROLOGUE: */\n\n" );
+}
+
+void
+init_emulation_routines ()
+{
+ dsp16xx_addhf3_libcall = (rtx) 0;
+ dsp16xx_subhf3_libcall = (rtx) 0;
+ dsp16xx_mulhf3_libcall = (rtx) 0;
+ dsp16xx_divhf3_libcall = (rtx) 0;
+ dsp16xx_cmphf3_libcall = (rtx) 0;
+ dsp16xx_fixhfhi2_libcall = (rtx) 0;
+ dsp16xx_floathihf2_libcall = (rtx) 0;
+ dsp16xx_neghf2_libcall = (rtx) 0;
+
+ dsp16xx_mulhi3_libcall = (rtx) 0;
+ dsp16xx_udivqi3_libcall = (rtx) 0;
+ dsp16xx_udivhi3_libcall = (rtx) 0;
+ dsp16xx_divqi3_libcall = (rtx) 0;
+ dsp16xx_divhi3_libcall = (rtx) 0;
+ dsp16xx_modqi3_libcall = (rtx) 0;
+ dsp16xx_modhi3_libcall = (rtx) 0;
+ dsp16xx_umodqi3_libcall = (rtx) 0;
+ dsp16xx_umodhi3_libcall = (rtx) 0;
+ dsp16xx_ashrhi3_libcall = (rtx) 0;
+ dsp16xx_ashlhi3_libcall = (rtx) 0;
+ dsp16xx_ucmphi2_libcall = (rtx) 0;
+ dsp16xx_lshrhi3_libcall = (rtx) 0;
+
+}
+void
+function_epilogue (file, size)
+FILE *file;
+int   size;
+{
+  int regno;
+  int initial_stack_dec = 0;
+  
+  fp = reg_names[FRAME_POINTER_REGNUM];
+  sp = reg_names[STACK_POINTER_REGNUM];
+  rr = reg_names[RETURN_ADDRESS_REGNUM];   /* return address register */
+  a1h = reg_names[REG_A1];
+  
+  fprintf( file, "\n\t/* FUNCTION EPILOGUE: */\n" );
+  
+  if (current_frame_info.args_size)
+    {
+      if (current_frame_info.args_size == 1)
+       fprintf (file, "\t*%s--\n", sp);
+      else
+       {
+         fprintf (file, "\t%s=%d\n\t*%s++%s\n", 
+                  reg_names[REG_J], -current_frame_info.args_size, sp, reg_names[REG_J]);
+       }
+    }
+  
+  if (ybase_regs_ever_used())
+    {
+      fprintf (file, "\t%s=%s\n", a1h, reg_names[REG_YBASE]);
+      if (TARGET_YBASE_HIGH)
+       fprintf (file, "\t%s=%sh+32\n", reg_names[REG_A1], a1h);
+      else
+       fprintf (file, "\t%s=%sh-32\n", reg_names[REG_A1], a1h);
+      fprintf (file, "\t%s=%s\n", reg_names[REG_YBASE], a1h);
+    }
+  
+  for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; --regno)
+    if (dsp16xx_call_saved_register(regno))
+      {
+#if OLD_REGISTER_SAVE
+       if (!initial_stack_dec)
+         {
+           initial_stack_dec = 1;
+           fprintf (file, "\t*%s--\n", sp);
+         }
+#endif
+
+#if OLD_REGISTER_SAVE
+       fprintf( file, "\t%s=*%s--\n", reg_names[regno], sp );
+#else
+       fprintf( file, "\t%s=pop(*%s)\n", reg_names[regno], sp );
+#endif
+      }
+  
+  /* If we restored any registers we have to account for the
+     initial pre-decrement. But only if we had any local variables
+     or spills. */
+#if OLD_REGISTER_SAVE  
+  if (initial_stack_dec) 
+    fprintf (file, "\t*%s++\n", sp);
+#endif
+  
+  if (current_frame_info.var_size)
+    {
+      if (current_frame_info.var_size == 1)
+       fprintf (file, "\t*%s--\n", sp);
+      else
+       {
+         fprintf (file, "\t%s=%d\n\t*%s++%s\n", 
+                  reg_names[REG_J], -current_frame_info.var_size, sp, reg_names[REG_J]);
+       }
+    }
+  
+  fprintf (file, "\treturn\n");
+  /* Reset the frame info for the next function */
+  current_frame_info = zero_frame_info;
+  init_emulation_routines ();
+}
+
+/* Emit insns to move operands[1] into operands[0].
+
+   Return 1 if we have written out everything that needs to be done to
+   do the move.  Otherwise, return 0 and the caller will emit the move
+   normally.  */
+
+int
+emit_move_sequence (operands, mode)
+     rtx *operands;
+     enum machine_mode mode;
+{
+  register rtx operand0 = operands[0];
+  register rtx operand1 = operands[1];
+
+  /* We can only store registers to memory.  */
+
+  if (GET_CODE (operand0) == MEM && GET_CODE (operand1) != REG)
+    operands[1] = force_reg (mode, operand1);
+
+  return 0;
+}
+
+void
+double_reg_from_memory (operands)
+rtx operands[];
+{
+    rtx xoperands[4];
+
+    if (GET_CODE(XEXP(operands[1],0)) == POST_INC)
+    {
+       output_asm_insn ("%u0=%1", operands);
+       output_asm_insn ("%w0=%1", operands);
+    }
+    else if (GET_CODE(XEXP(operands[1],0)) == POST_DEC)
+    {
+       xoperands[1] = XEXP (XEXP (operands[1], 0), 0);
+       xoperands[0] = operands[0];
+       
+       /* We can't use j anymore since the compiler can allocate it. */
+/*     output_asm_insn ("j=-3\n\t%u0=*%1++\n\t%w0=*%1++j", xoperands); */
+       output_asm_insn ("%u0=*%1++\n\t%w0=*%1--\n\t*%1--\n\t*%1--", xoperands);
+    }
+    else if (GET_CODE(XEXP(operands[1],0)) == PLUS)
+    {
+      rtx addr;
+      rtx base;
+      int offset;
+
+      output_asm_insn ("%u0=%1", operands);
+
+
+      /* In order to print out the least significant word we must
+        use 'offset + 1'.  */
+      addr = XEXP (operands[1], 0);
+      if (GET_CODE (XEXP(addr,0)) == CONST_INT)
+       offset = INTVAL(XEXP(addr,0)) + 1;
+      else if (GET_CODE (XEXP(addr,1)) == CONST_INT)
+       offset = INTVAL(XEXP(addr,1)) + 1;
+
+      fprintf (asm_out_file, "\t%s=*(%d)\n", reg_names[REGNO(operands[0]) + 1], offset + 31);
+    }
+    else
+    {
+       xoperands[1] = XEXP(operands[1],0);
+       xoperands[0] = operands[0];
+
+       output_asm_insn ("%u0=*%1++\n\t%w0=*%1--", xoperands);
+    }
+}
+
+
+void
+double_reg_to_memory (operands)
+rtx operands[];
+{
+    rtx xoperands[4];
+
+    if (GET_CODE(XEXP(operands[0],0)) == POST_INC)
+    {
+       output_asm_insn ("%0=%u1", operands);
+       output_asm_insn ("%0=%w1", operands);
+    }
+    else if (GET_CODE(XEXP(operands[0],0)) == POST_DEC)
+    {
+       xoperands[0] = XEXP (XEXP (operands[0], 0), 0);
+       xoperands[1] = operands[1];
+       
+       /* We can't use j anymore since the compiler can allocate it. */
+
+/*     output_asm_insn ("j=-3\n\t*%0++=%u1\n\t*%0++j=%w1", xoperands); */
+       output_asm_insn ("*%0++=%u1\n\t*%0--=%w1\n\t*%0--\n\t*%0--", xoperands);
+
+    }
+    else if (GET_CODE(XEXP(operands[0],0)) == PLUS)
+    {
+      rtx addr;
+      int offset;
+
+      output_asm_insn ("%0=%u1", operands);
+
+      /* In order to print out the least significant word we must
+        use 'offset + 1'.  */
+      addr = XEXP (operands[0], 0);
+      if (GET_CODE (XEXP(addr,0)) == CONST_INT)
+       offset = INTVAL(XEXP(addr,0)) + 1;
+      else if (GET_CODE (XEXP(addr,1)) == CONST_INT)
+       offset = INTVAL(XEXP(addr,1)) + 1;
+      else
+       fatal ("Illegal addressing mode");
+
+      fprintf (asm_out_file, "\t*(%d)=%s\n", offset + 31, reg_names[REGNO(operands[1]) + 1]);
+    }
+    else
+    {
+       xoperands[0] = XEXP(operands[0],0);
+       xoperands[1] = operands[1];
+
+       output_asm_insn ("*%0++=%u1\n\t*%0--=%w1", xoperands);
+    }
+}
+
+void
+override_options ()
+{
+  if (chip_name == (char *) 0)
+    chip_name = DEFAULT_CHIP_NAME;
+
+  if (text_seg_name == (char *) 0)
+    text_seg_name = DEFAULT_TEXT_SEG_NAME;
+  
+  if (data_seg_name == (char *) 0)
+    data_seg_name = DEFAULT_DATA_SEG_NAME;
+  
+  if (bss_seg_name == (char *) 0)
+    bss_seg_name = DEFAULT_BSS_SEG_NAME;
+  
+  if (const_seg_name == (char *) 0)
+    const_seg_name = DEFAULT_CONST_SEG_NAME;
+  
+  save_chip_name = (char *) xmalloc (strlen(chip_name) + 1);
+  strcpy (save_chip_name, chip_name);
+
+  rsect_text = (char *) xmalloc (strlen(".rsect ") + 
+                                strlen(text_seg_name) + 3);
+  rsect_data = (char *) xmalloc (strlen(".rsect ") + 
+                                strlen(data_seg_name) + 3);
+  rsect_bss = (char *) xmalloc (strlen(".rsect ") + 
+                               strlen(bss_seg_name) + 3);
+  rsect_const = (char *) xmalloc (strlen(".rsect ") + 
+                                 strlen(const_seg_name) + 3);
+  
+  sprintf (rsect_text, ".rsect \"%s\"", text_seg_name);
+  sprintf (rsect_data, ".rsect \"%s\"", data_seg_name);
+  sprintf (rsect_bss,  ".rsect \"%s\"", bss_seg_name);
+  sprintf (rsect_const, ".rsect \"%s\"", const_seg_name);
+  
+  if (optimize)
+    {
+      if (TARGET_OPTIMIZE_SPEED)
+       {
+         flag_unroll_loops = 1;
+         flag_inline_functions = 1;
+       }
+    }
+}
+
+enum rtx_code save_next_cc_user_code;
+
+enum rtx_code
+next_cc_user_code (insn)
+rtx insn;
+{
+  if ( !(insn = next_cc0_user (insn)))
+    abort ();
+  else if (GET_CODE (insn) == JUMP_INSN
+          && GET_CODE (PATTERN (insn)) == SET
+          && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)
+    return GET_CODE (XEXP (SET_SRC (PATTERN (insn)), 0));
+  else if (GET_CODE (insn) == INSN
+          && GET_CODE (PATTERN (insn)) == SET
+          && comparison_operator (SET_SRC (PATTERN (insn)), VOIDmode))
+    return GET_CODE (SET_SRC (PATTERN (insn)));
+  else
+    abort ();
+}
+
+void
+print_operand(file, op, letter)
+FILE *file;
+rtx op;
+int letter;
+{
+    enum rtx_code code;
+
+    code = GET_CODE(op);
+
+    switch (letter)
+    {
+       case 'I':
+         code = reverse_condition (code);
+         /* Fallthrough */
+
+       case 'C':
+          if (code == EQ) 
+          { 
+             fputs ("eq", file); 
+             return; 
+         }
+          else if (code == NE)  
+         { 
+             fputs ("ne", file); 
+             return; 
+         }
+          else if (code == GT || code == GTU)  
+         { 
+             fputs ("gt", file); 
+             return; 
+         }
+          else if (code == LT || code == LTU)  
+         { 
+             fputs ("mi", file); 
+             return; 
+         }
+          else if (code == GE || code == GEU)  
+         {
+             fputs ("pl", file); 
+             return; 
+         }
+          else if (code == LE || code == LEU)  
+         { 
+             fputs ("le", file); 
+             return; 
+         }
+          else 
+             abort ();
+         break;
+
+       default:
+          break;  
+    }
+
+    if( code == REG )
+    {
+       /* Print the low half of a 32-bit register pair */
+        if (letter == 'w')
+           fprintf( file, "%s", reg_names[REGNO(op)+1] );
+        else if (letter == 'u' || !letter)
+           fprintf( file, "%s", reg_names[REGNO(op)]);
+       else if (letter == 'b')
+           fprintf ( file, "%sh", reg_names[REGNO(op)]);
+       else if (letter == 'm')
+         fprintf (file, "%s", himode_reg_name[REGNO(op)]);
+        else
+           fatal("Bad register extension code");
+    }
+    else if( code == MEM )
+        output_address( XEXP(op,0) );
+    else if( code == CONST_INT )
+    { 
+        if( letter == 'H' )
+            fprintf( file, "0x%x", (INTVAL(op) & 0xffff) );
+       else if (letter == 'h')
+            fprintf( file, "%d", INTVAL (op) );
+        else if( letter == 'U' )
+            fprintf( file, "0x%x", ((INTVAL(op) & 0xffff0000) >> 16) & 0xffff );
+        else
+           output_addr_const( file, op );
+    }
+    else if( code == CONST_DOUBLE && GET_MODE(op) != DImode )
+    {
+         union { double d; int i[2]; } u;
+         union { float f; int i; } u1;
+         u.i[0] = CONST_DOUBLE_LOW (op);
+         u.i[1] = CONST_DOUBLE_HIGH (op);
+         u1.f = u.d;
+          fprintf( file, "0x%x", u1.i );
+    }
+    else output_addr_const( file, op);
+}
+
+
+void
+print_operand_address(file, addr)
+FILE *file;
+rtx addr;
+{
+  rtx base;
+  int offset;
+  
+  switch (GET_CODE (addr))
+    {
+    case REG:
+      fprintf (file, "*%s", reg_names[REGNO (addr)]);
+      break;
+    case POST_DEC:
+      fprintf (file, "*%s--", reg_names[REGNO (XEXP (addr, 0))]);
+      break;
+    case POST_INC:
+      fprintf (file, "*%s++", reg_names[REGNO (XEXP (addr, 0))]);
+      break;
+    case PLUS:
+      if (GET_CODE (XEXP(addr,0)) == CONST_INT)
+       offset = INTVAL(XEXP(addr,0)), base = XEXP(addr,1);
+      else if (GET_CODE (XEXP(addr,1)) == CONST_INT)
+       offset = INTVAL(XEXP(addr,1)), base = XEXP(addr,0);
+      if (GET_CODE (base) == REG && REGNO(base) == STACK_POINTER_REGNUM)
+       {
+         if (offset >= -31 && offset <= 0)
+           offset = 31 + offset;
+         else
+           fatal ("Illegal offset in ybase addressing");
+       }
+      else
+       fatal ("Illegal register in ybase addresing");
+      
+      fprintf (file, "*(%d)", offset);
+      break;
+      
+    default:
+      if( FITS_5_BITS( addr ) )
+       fprintf( file, "*(0x%x)", (INTVAL(addr) & 0x20) );
+      else
+       output_addr_const(file, addr);
+    }
+}
+
+void
+output_dsp16xx_float_const(operands)
+rtx *operands;
+{
+  rtx dst = operands[0];
+  rtx src = operands[1];
+  
+#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+  REAL_VALUE_TYPE d;
+  long value;
+  
+  REAL_VALUE_FROM_CONST_DOUBLE (d, src);
+  REAL_VALUE_TO_TARGET_SINGLE (d, value);
+  
+  operands[1] = gen_rtx (CONST_INT, VOIDmode, value);
+  output_asm_insn ("%u0=%U1\n\t%w0=%H1", operands);
+#else
+  fatal ("inline float constants not supported on this host");
+#endif
+}
+
+int
+reg_save_size ()
+{
+  int reg_save_size = 0;
+ int regno;
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (dsp16xx_call_saved_register (regno))
+      {
+       reg_save_size += UNITS_PER_WORD;
+      }
+
+  return (reg_save_size);
+}
+
+int
+dsp16xx_starting_frame_offset()
+{
+  int reg_save_size = 0;
+ int regno;
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+    if (dsp16xx_call_saved_register (regno))
+      {
+       reg_save_size += UNITS_PER_WORD;
+      }
+
+  return (reg_save_size);
+}
+
+int
+initial_frame_pointer_offset()
+{
+  int frame_size;
+  int regno;
+  int offset = 0;
+  
+  offset = compute_frame_size (get_frame_size());
+
+#ifdef STACK_GROWS_DOWNWARD
+  return (offset);
+#else
+  return (-offset);
+#endif
+}
+
+/* Generate the minimum number of 1600 core shift instructions
+   to shift by 'shift_amount'. */
+
+#if 0
+void
+emit_1600_core_shift (shift_op, operands, shift_amount, mode)
+enum rtx_code shift_op;
+rtx *operands;
+int shift_amount;
+enum machine_mode mode;
+{
+  int quotient;
+  int i;
+  int first_shift_emitted = 0;
+  
+  while (shift_amount != 0)
+    {
+      if (shift_amount/16)
+       {
+         quotient = shift_amount/16;
+         shift_amount = shift_amount - (quotient * 16);
+         for (i = 0; i < quotient; i++)
+           emit_insn (gen_rtx (SET, VOIDmode, operands[0],
+                               gen_rtx (shift_op, mode, 
+                                        first_shift_emitted ? operands[0] : operands[1],
+                                        gen_rtx (CONST_INT, VOIDmode, 16))));
+         first_shift_emitted = 1;
+       }
+      else if (shift_amount/8)
+       {
+         quotient = shift_amount/8;
+         shift_amount = shift_amount - (quotient * 8);
+         for (i = 0; i < quotient; i++)
+           emit_insn (gen_rtx (SET, VOIDmode, operands[0],
+                               gen_rtx (shift_op, mode, 
+                                        first_shift_emitted ? operands[0] : operands[1],
+                                        gen_rtx (CONST_INT, VOIDmode, 8))));
+         first_shift_emitted = 1;
+       }
+      else if (shift_amount/4)
+       {
+         quotient = shift_amount/4;
+         shift_amount = shift_amount - (quotient * 4);
+         for (i = 0; i < quotient; i++)
+           emit_insn (gen_rtx (SET, VOIDmode, operands[0],
+                               gen_rtx (shift_op, mode, 
+                                        first_shift_emitted ? operands[0] : operands[1],
+                                        gen_rtx (CONST_INT, VOIDmode, 4))));
+         first_shift_emitted = 1;
+       }
+      else if (shift_amount/1)
+       {
+         quotient = shift_amount/1;
+         shift_amount = shift_amount - (quotient * 1);
+         for (i = 0; i < quotient; i++)
+           emit_insn (gen_rtx (SET, VOIDmode, operands[0],
+                               gen_rtx (shift_op, mode, 
+                                        first_shift_emitted ? operands[0] : operands[1],
+                                        gen_rtx (CONST_INT, VOIDmode, 1))));
+         first_shift_emitted = 1;
+       }
+    }
+}
+#else
+void
+emit_1600_core_shift (shift_op, operands, shift_amount)
+enum rtx_code shift_op;
+rtx *operands;
+int shift_amount;
+{
+  int quotient;
+  int i;
+  int first_shift_emitted = 0;
+  char **shift_asm_ptr;
+  char **shift_asm_ptr_first;
+
+  if (shift_op == ASHIFT)
+    {
+      shift_asm_ptr = ashift_left_asm;
+      shift_asm_ptr_first = ashift_left_asm_first;
+    }
+  else if (shift_op == ASHIFTRT)
+    {
+      shift_asm_ptr = ashift_right_asm;
+      shift_asm_ptr_first = ashift_right_asm_first;
+    }
+  else if (shift_op == LSHIFTRT)
+    {
+      shift_asm_ptr = lshift_right_asm;
+      shift_asm_ptr_first = lshift_right_asm_first;
+    }
+  else
+    fatal ("Illegal shift operator in emit_1600_core_shift");
+
+  while (shift_amount != 0)
+    {
+      if (shift_amount/16)
+       {
+         quotient = shift_amount/16;
+         shift_amount = shift_amount - (quotient * 16);
+         for (i = 0; i < quotient; i++)
+           output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_16]
+                             : shift_asm_ptr_first[SHIFT_INDEX_16]), operands);
+         first_shift_emitted = 1;
+       }
+      else if (shift_amount/8)
+       {
+         quotient = shift_amount/8;
+         shift_amount = shift_amount - (quotient * 8);
+         for (i = 0; i < quotient; i++)
+           output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_8]
+                             : shift_asm_ptr_first[SHIFT_INDEX_8]), operands);
+         first_shift_emitted = 1;
+       }
+      else if (shift_amount/4)
+       {
+         quotient = shift_amount/4;
+         shift_amount = shift_amount - (quotient * 4);
+         for (i = 0; i < quotient; i++)
+           output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_4]
+                             : shift_asm_ptr_first[SHIFT_INDEX_4]), operands);
+         first_shift_emitted = 1;
+       }
+      else if (shift_amount/1)
+       {
+         quotient = shift_amount/1;
+         shift_amount = shift_amount - (quotient * 1);
+         for (i = 0; i < quotient; i++)
+           output_asm_insn ((first_shift_emitted ? shift_asm_ptr[SHIFT_INDEX_1]
+                             : shift_asm_ptr_first[SHIFT_INDEX_1]), operands);
+         first_shift_emitted = 1;
+       }
+    }
+}
+#endif
+void
+  asm_output_common(file, name, size, rounded)
+FILE *file;
+char *name;
+int size;
+int rounded;
+{
+    bss_section ();
+    ASM_GLOBALIZE_LABEL (file, name);
+    assemble_name (file, name);
+    fputs (":", file);
+    if (rounded > 1)
+       fprintf (file, "%d * int\n", rounded);
+    else
+       fprintf (file, "int\n");
+}
+
+void
+asm_output_local(file, name, size, rounded)
+FILE *file;
+char *name;
+int size;
+int rounded;
+{
+    bss_section ();
+    assemble_name (file, name);
+    fputs (":", file);
+    if (rounded > 1)
+       fprintf (file, "%d * int\n", rounded);
+    else
+       fprintf (file, "int\n");
+}
+
+void
+asm_output_float (file, fp_const)
+FILE *file;
+double fp_const;
+{
+#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+      REAL_VALUE_TYPE d = fp_const;
+      long value;
+
+      REAL_VALUE_TO_TARGET_SINGLE (d, value);
+      fputs ("\tint ", file);
+#ifdef WORDS_BIG_ENDIAN
+      fprintf (file, "0x%-4.4x, 0x%-4.4x", (value >> 16) & 0xffff, (value & 0xffff));
+#else
+      fprintf (file, "0x%-4.4x, 0x%-4.4x", (value & 0xffff), (value >> 16) & 0xffff);
+#endif
+      fputs ("\n", file);
+#else
+      fatal ("inline float constants not supported on this host");
+#endif
+}
+
+void
+asm_output_long (file, value)
+FILE *file;
+long value;
+{
+      fputs ("\tint ", file);
+#ifdef WORDS_BIG_ENDIAN
+      fprintf (file, "0x%-4.4x, 0x%-4.4x", (value >> 16) & 0xffff, (value & 0xffff));
+#else
+      fprintf (file, "0x%-4.4x, 0x%-4.4x", (value & 0xffff), (value >> 16) & 0xffff);
+#endif
+      fputs ("\n", file);
+}
+
+int
+dsp16xx_address_cost (addr)
+rtx addr;
+{
+    switch (GET_CODE (addr))
+    {
+         default:
+            break;
+
+         case REG:
+            return 1;
+
+         case CONST:
+            {
+               rtx offset = const0_rtx;
+               addr = eliminate_constant_term (addr, &offset);
+
+               if (GET_CODE (addr) == LABEL_REF)
+                   return 2;
+
+               if (GET_CODE (addr) != SYMBOL_REF)
+                   return 4;
+
+               if (INTVAL (offset) == 0)
+                   return 2;
+             }
+            /* fall through */
+
+         case POST_INC: case POST_DEC:
+            return (GET_MODE (addr) == QImode ? 1 : 2);
+
+         case SYMBOL_REF: case LABEL_REF:
+            return 2;
+
+         case PLUS:
+         {
+            register rtx plus0 = XEXP (addr, 0);
+            register rtx plus1 = XEXP (addr, 1);
+            
+            if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
+            {
+                plus0 = XEXP (addr, 1);
+                plus1 = XEXP (addr, 0);
+            }
+            
+            if (GET_CODE (plus0) != REG)
+                break;
+            
+            switch (GET_CODE (plus1))
+            {
+                  default:
+                     break;
+                
+                  case CONST_INT:
+                     return 4;
+
+                  case CONST:
+                  case SYMBOL_REF:
+                  case LABEL_REF:
+                     return dsp16xx_address_cost (plus1) + 1;
+            }
+         }
+     }
+            
+     return 4;
+}
+
+\f
+/* Determine whether a function argument is passed in a register, and
+   which register.
+
+   The arguments are CUM, which summarizes all the previous
+   arguments; MODE, the machine mode of the argument; TYPE,
+   the data type of the argument as a tree node or 0 if that is not known
+   (which happens for C support library functions); and NAMED,
+   which is 1 for an ordinary argument and 0 for nameless arguments that
+   correspond to `...' in the called function's prototype.
+
+   The value of the expression should either be a `reg' RTX for the
+   hard register in which to pass the argument, or zero to pass the
+   argument on the stack.
+
+   On the dsp1610 the first four words of args are normally in registers
+   and the rest are pushed. If we a long or on float mode, the argument
+   must begin on a even register boundary
+
+   Note that FUNCTION_ARG and FUNCTION_INCOMING_ARG were different.
+   For structures that are passed in memory, but could have been
+   passed in registers, we first load the structure into the
+   register, and then when the last argument is passed, we store
+   the registers into the stack locations.  This fixes some bugs
+   where GCC did not expect to have register arguments, followed */
+
+
+struct rtx_def *
+dsp16xx_function_arg (args_so_far, mode, type, named)
+     CUMULATIVE_ARGS args_so_far;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  if (TARGET_REGPARM)
+    {
+      if ((args_so_far & 1) != 0
+         && (mode == HImode || GET_MODE_CLASS(mode) == MODE_FLOAT))
+       args_so_far++;
+
+      if (named && args_so_far < 4 && !MUST_PASS_IN_STACK (mode,type))
+       return gen_rtx (REG, mode, args_so_far + FIRST_REG_FOR_FUNCTION_ARG);
+      else
+       return (struct rtx_def *) 0;
+    }
+  else
+    return (struct rtx_def *) 0;
+}
+
+/* Advance the argument to the next argument position.  */
+
+void
+dsp16xx_function_arg_advance (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;     /* current arg information */
+     enum machine_mode mode;   /* current arg mode */
+     tree type;                        /* type of the argument or 0 if lib support */
+     int named;                        /* whether or not the argument was named */
+{
+  if (TARGET_REGPARM)
+    {
+      if ((*cum & 1) != 0
+         && (mode == HImode || GET_MODE_CLASS(mode) == MODE_FLOAT))
+       *cum += 1;
+
+      if (mode != BLKmode)
+       *cum += GET_MODE_SIZE (mode);
+      else
+       *cum += int_size_in_bytes (type);
+    }
+}
+
+void
+dsp16xx_file_start ()
+{
+  fprintf (asm_out_file, "#include <%s.h>\n", save_chip_name);
+#if 0
+       if (TARGET_BMU)
+               fprintf (asm_out_file, "#include <1610.h>\n");
+#endif
+}
+
+rtx
+gen_tst_reg (x)
+     rtx x;
+{
+  enum machine_mode mode;
+
+  mode = GET_MODE (x);
+
+  if (mode == QImode)
+    {
+         emit_insn (gen_rtx (PARALLEL, VOIDmode,
+                       gen_rtvec (2,
+                                  gen_rtx (SET, VOIDmode, cc0_rtx, x),
+                                  gen_rtx (CLOBBER, VOIDmode,
+                                           gen_rtx (SCRATCH, QImode, 0)))));
+       }
+  else if (mode == HImode)
+    emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, x));
+  else
+    fatal ("Illegal mode for gen_tst_reg");
+
+  return cc0_rtx;
+}
+
+rtx
+gen_compare_reg (code, x, y)
+     enum rtx_code code;
+     rtx x, y;
+{
+  enum machine_mode mode;
+
+  mode = GET_MODE (x);
+  /* For floating point compare insns, a call is generated so don't
+     do anything here. */
+
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+    return cc0_rtx;
+
+  if (mode == QImode)
+    {
+      if (code == GTU || code == GEU ||
+         code == LTU || code == LEU)
+       {
+         emit_insn (gen_rtx (PARALLEL, VOIDmode,
+                       gen_rtvec (3,
+                                  gen_rtx (SET, VOIDmode, cc0_rtx,
+                                           gen_rtx (COMPARE, mode, x, y)),
+                                  gen_rtx (CLOBBER, VOIDmode,
+                                           gen_rtx (SCRATCH, QImode, 0)),
+                                  gen_rtx (CLOBBER, VOIDmode,
+                                           gen_rtx (SCRATCH, QImode, 0)))));
+       }
+      else
+       {
+         emit_insn (gen_rtx (PARALLEL, VOIDmode,
+                       gen_rtvec (3,
+                                  gen_rtx (SET, VOIDmode, cc0_rtx,
+                                           gen_rtx (COMPARE, mode, x, y)),
+                                  gen_rtx (CLOBBER, VOIDmode,
+                                           gen_rtx (SCRATCH, QImode, 0)),
+                                  gen_rtx (CLOBBER, VOIDmode,
+                                           gen_rtx (SCRATCH, QImode, 0)))));
+       }
+    }
+  else if (mode == HImode)
+    {
+      if (code == GTU || code == GEU ||
+         code == LTU || code == LEU)
+       {
+#if 1
+         emit_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (5,
+                            gen_rtx (SET, VOIDmode, cc0_rtx, gen_rtx (COMPARE, VOIDmode, x, y)),
+                            gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)),
+                            gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)),
+                            gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)),
+                            gen_rtx (CLOBBER, VOIDmode, gen_rtx (SCRATCH, QImode, 0)))));
+#else
+         if (!dsp16xx_ucmphi2_libcall)
+           dsp16xx_ucmphi2_libcall = gen_rtx (SYMBOL_REF, Pmode, UCMPHI2_LIBCALL);
+         emit_library_call (dsp16xx_ucmphi2_libcall, 1, HImode, 2,
+                            x, HImode, y, HImode);
+         emit_insn (gen_tsthi_1 (copy_to_reg(hard_libcall_value (HImode))));
+#endif
+       }
+      else
+       emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx,
+                           gen_rtx (COMPARE, VOIDmode, force_reg(HImode, x), 
+                                    force_reg(HImode,y))));
+    }
+  else
+    fatal ("Illegal mode for integer comparison in gen_compare_reg");
+
+  return cc0_rtx;
+}
+
+char *
+output_block_move (operands)
+     rtx operands[];
+{
+  int loop_count = INTVAL(operands[2]);
+  rtx xoperands[4];
+
+  fprintf (asm_out_file, "\tdo %d {\n", loop_count);
+  xoperands[0] = operands[4];
+  xoperands[1] = operands[1];
+  output_asm_insn ("%0=*%1++", xoperands);
+
+  xoperands[0] = operands[0];
+  xoperands[1] = operands[4];
+  output_asm_insn ("*%0++=%1", xoperands);
+
+  fprintf (asm_out_file, "\t}\n");
+  return "";
+}