;; GCC machine description for Matsushita MN10300
;; Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
;; Contributed by Jeff Law (law@cygnus.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, 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;; The original PO technology requires these to be ordered by speed,
;; so that assigner will pick the fastest.

;; See file "rtl.def" for documentation on define_insn, match_*, et. al.

;; Condition code settings.
;; none - insn does not affect cc
;; none_0hit - insn does not affect cc but it does modify operand 0
;;	This attribute is used to keep track of when operand 0 changes.
;; 	See the description of NOTICE_UPDATE_CC for more info.
;; set_znv - insn sets z,n,v to usable values; c is unusable.
;; set_zn  - insn sets z,n to usable values; v,c are unusable.
;; compare - compare instruction
;; invert -- like compare, but flags are inverted.
;; clobber - value of cc is unknown
(define_attr "cc" "none,none_0hit,set_znv,set_zn,compare,clobber,invert"
  (const_string "clobber"))

;; ----------------------------------------------------------------------
;; MOVE INSTRUCTIONS
;; ----------------------------------------------------------------------

;; movqi

(define_expand "movqi"
  [(set (match_operand:QI 0 "general_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand0, QImode)
      && !register_operand (operand1, QImode))
    operands[1] = copy_to_mode_reg (QImode, operand1);
}")

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=dx,*a,dx,*a,dx,*a,dx,*a,dx,m")
	(match_operand:QI 1 "general_operand" "0,0,I,I,a,dx,dxi,ia,m,dx"))]
  "register_operand (operands[0], QImode)
   || register_operand (operands[1], QImode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"nop\";
    case 2:
      return \"clr %0\";
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}

      return \"mov %1,%0\";
    case 8:
    case 9:
      return \"movbu %1,%0\";
    }
}"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movhi

(define_expand "movhi"
  [(set (match_operand:HI 0 "general_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, HImode)
      && !register_operand (operand0, HImode))
    operands[1] = copy_to_mode_reg (HImode, operand1);
}")

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=dx,*a,dx,*a,dx,*a,dx,*a,dx,m")
	(match_operand:HI 1 "general_operand" "0,0,I,I,a,dx,dxi,ia,m,dx"))]
  "register_operand (operands[0], HImode)
   || register_operand (operands[1], HImode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"nop\";
    case 2:
      return \"clr %0\";
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}
      return \"mov %1,%0\";
    case 8:
    case 9:
      return \"movhu %1,%0\";
    }
}"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

;; movsi and helpers

;; We use this to handle addition of two values when one operand is the
;; stack pointer and the other is a memory reference of some kind.  Reload
;; does not handle them correctly without this expander.
(define_expand "reload_insi"
  [(set (match_operand:SI 0 "register_operand" "=a")
	(match_operand:SI 1 "impossible_plus_operand" ""))
   (clobber (match_operand:SI 2 "register_operand" "=&r"))]
  ""
  "
{
  if (XEXP (operands[1], 0) == stack_pointer_rtx)
    {
      if (GET_CODE (XEXP (operands[1], 1)) == SUBREG
	  && (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 1)))
	      > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 1))))))
	emit_move_insn (operands[2],
			gen_rtx_ZERO_EXTEND
			(GET_MODE (XEXP (operands[1], 1)),
			 SUBREG_REG (XEXP (operands[1], 1))));
      else
	emit_move_insn (operands[2], XEXP (operands[1], 1));
      emit_move_insn (operands[0], XEXP (operands[1], 0));
    }
  else
    {
      if (GET_CODE (XEXP (operands[1], 0)) == SUBREG
	  && (GET_MODE_SIZE (GET_MODE (XEXP (operands[1], 0)))
	      > GET_MODE_SIZE (GET_MODE (SUBREG_REG (XEXP (operands[1], 0))))))
	emit_move_insn (operands[2],
			gen_rtx_ZERO_EXTEND
			(GET_MODE (XEXP (operands[1], 0)),
			 SUBREG_REG (XEXP (operands[1], 0))));
      else
	emit_move_insn (operands[2], XEXP (operands[1], 0));
      emit_move_insn (operands[0], XEXP (operands[1], 1));
    }
  emit_insn (gen_addsi3 (operands[0], operands[0], operands[2]));
  DONE;
}")

(define_expand "movsi"
  [(set (match_operand:SI 0 "general_operand" "")
	(match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SImode)
      && !register_operand (operand0, SImode))
    operands[1] = copy_to_mode_reg (SImode, operand1);
}")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand"
				"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax,axR,y")
	(match_operand:SI 1 "general_operand"
				"0,0,I,I,dx,ax,dx,ax,dixm,aixm,dixm,aixm,xy,axR"))]
  "register_operand (operands[0], SImode)
   || register_operand (operands[1], SImode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"nop\";
    case 2:
      return \"clr %0\";
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
    case 10:
    case 11:
    case 12:
    case 13:
      if (GET_CODE (operands[1]) == CONST_DOUBLE)
	{
	  rtx xoperands[2];
	  xoperands[0] = operands[0];
	  xoperands[1] = GEN_INT (CONST_DOUBLE_LOW (operands[1]));
	  output_asm_insn (\"mov %1,%0\", xoperands);
	  return \"\";
	}
      return \"mov %1,%0\";
    }
}"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_expand "movsf"
  [(set (match_operand:SF 0 "general_operand" "")
	(match_operand:SF 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, SFmode)
      && !register_operand (operand0, SFmode))
    operands[1] = copy_to_mode_reg (SFmode, operand1);
}")

(define_insn ""
  [(set (match_operand:SF 0 "general_operand" "=dx,ax,dx,a,daxm,dax")
	(match_operand:SF 1 "general_operand" "0,0,G,G,dax,daxim"))]
  "register_operand (operands[0], SFmode)
   || register_operand (operands[1], SFmode)"
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"nop\";
    case 2:
      return \"clr %0\";
    case 3:
    case 4:
    case 5:
      return \"mov %1,%0\";
    }
}"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit")])

(define_expand "movdi"
  [(set (match_operand:DI 0 "general_operand" "")
	(match_operand:DI 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, DImode)
      && !register_operand (operand0, DImode))
    operands[1] = copy_to_mode_reg (DImode, operand1);
}")

(define_insn ""
  [(set (match_operand:DI 0 "general_operand"
				"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax")
	(match_operand:DI 1 "general_operand"
				"0,0,I,I,dx,ax,dx,ax,dxim,axim,dxim,axim"))]
  "register_operand (operands[0], DImode)
   || register_operand (operands[1], DImode)"
  "*
{
  long val[2];
  REAL_VALUE_TYPE rv;

  switch (which_alternative)
    {
      case 0:
      case 1:
	return \"nop\";

      case 2:
	return \"clr %L0\;clr %H0\";

      case 3:
	if (rtx_equal_p (operands[0], operands[1]))
	  return \"sub %L1,%L0\;mov %L0,%H0\";
	else
	  return \"mov %1,%L0\;mov %L0,%H0\";
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 11:
	if (GET_CODE (operands[1]) == CONST_INT)
	  {
	    val[0] = INTVAL (operands[1]);
	    val[1] = val[0] < 0 ? -1 : 0;
	  }
	if (GET_CODE (operands[1]) == CONST_DOUBLE)
	  {
	    if (GET_MODE (operands[1]) == DFmode)
	      {
		REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
		REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
	      }
	    else if (GET_MODE (operands[1]) == VOIDmode
		     || GET_MODE (operands[1]) == DImode)
	      {
		val[0] = CONST_DOUBLE_LOW (operands[1]);
		val[1] = CONST_DOUBLE_HIGH (operands[1]);
	      }
	  }

	if (GET_CODE (operands[1]) == MEM
	    && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
	  {
	    rtx temp = operands[0];

	    while (GET_CODE (temp) == SUBREG)
	      temp = SUBREG_REG (temp);

	    if (GET_CODE (temp) != REG)
	      abort ();

	    if (reg_overlap_mentioned_p (gen_rtx_REG (SImode, REGNO (temp)),
					 XEXP (operands[1], 0)))
	      return \"mov %H1,%H0\;mov %L1,%L0\";
	    else
	      return \"mov %L1,%L0\;mov %H1,%H0\";
	      
	  }
	else if (GET_CODE (operands[1]) == MEM
		 && CONSTANT_ADDRESS_P (XEXP (operands[1], 0))
		 && REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS)
	  {
	    rtx xoperands[2];

	    xoperands[0] = operands[0];
	    xoperands[1] = XEXP (operands[1], 0);

	    output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\",
			     xoperands);
	    return \"\";
	  }
	else
	  {
	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[0] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %L0\", operands);
		else
		  output_asm_insn (\"mov %L1,%L0\", operands);
	      }
	    else
	      output_asm_insn (\"mov %L1,%L0\", operands);

	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[1] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %H0\", operands);
		else
		  output_asm_insn (\"mov %H1,%H0\", operands);
	      }
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && val[0] == val[1])
	      output_asm_insn (\"mov %L0,%H0\", operands);
	    else
	      output_asm_insn (\"mov %H1,%H0\", operands);
	    return \"\";
	  }
    }
}"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])

(define_expand "movdf"
  [(set (match_operand:DF 0 "general_operand" "")
	(match_operand:DF 1 "general_operand" ""))]
  ""
  "
{
  /* One of the ops has to be in a register */
  if (!register_operand (operand1, DFmode)
      && !register_operand (operand0, DFmode))
    operands[1] = copy_to_mode_reg (DFmode, operand1);
}")

(define_insn ""
  [(set (match_operand:DF 0 "general_operand"
				"=dx,ax,dx,a,dxm,dxm,axm,axm,dx,dx,ax,ax")
	(match_operand:DF 1 "general_operand"
				"0,0,G,G,dx,ax,dx,ax,dxim,axim,dxim,axim"))]
  "register_operand (operands[0], DFmode)
   || register_operand (operands[1], DFmode)"
  "*
{
  long val[2];
  REAL_VALUE_TYPE rv;

  switch (which_alternative)
    {
      case 0:
      case 1:
	return \"nop\";

      case 2:
	return \"clr %L0\;clr %H0\";

      case 3:
	 if (rtx_equal_p (operands[0], operands[1]))
	   return \"sub %L1,%L0\;mov %L0,%H0\";
	 else
	   return \"mov %1,%L0\;mov %L0,%H0\";
      case 4:
      case 5:
      case 6:
      case 7:
      case 8:
      case 9:
      case 10:
      case 11:
	if (GET_CODE (operands[1]) == CONST_INT)
	  {
	    val[0] = INTVAL (operands[1]);
	    val[1] = val[0] < 0 ? -1 : 0;
	  }
	if (GET_CODE (operands[1]) == CONST_DOUBLE)
	  {
	    if (GET_MODE (operands[1]) == DFmode)
	      {
		REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
		REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
	      }
	    else if (GET_MODE (operands[1]) == VOIDmode
		     || GET_MODE (operands[1]) == DImode)
	      {
		val[0] = CONST_DOUBLE_LOW (operands[1]);
		val[1] = CONST_DOUBLE_HIGH (operands[1]);
	      }
	  }

	if (GET_CODE (operands[1]) == MEM
	    && reg_overlap_mentioned_p (operands[0], XEXP (operands[1], 0)))
	  {
	    rtx temp = operands[0];

	    while (GET_CODE (temp) == SUBREG)
	      temp = SUBREG_REG (temp);

	    if (GET_CODE (temp) != REG)
	      abort ();

	    if (reg_overlap_mentioned_p (gen_rtx_REG (SImode, REGNO (temp)),
					 XEXP (operands[1], 0)))
	      return \"mov %H1,%H0\;mov %L1,%L0\";
	    else
	      return \"mov %L1,%L0\;mov %H1,%H0\";
	      
	  }
	else if (GET_CODE (operands[1]) == MEM
		 && CONSTANT_ADDRESS_P (XEXP (operands[1], 0))
		 && REGNO_REG_CLASS (REGNO (operands[0])) == ADDRESS_REGS)
	  {
	    rtx xoperands[2];

	    xoperands[0] = operands[0];
	    xoperands[1] = XEXP (operands[1], 0);

	    output_asm_insn (\"mov %1,%L0\;mov (4,%L0),%H0\;mov (%L0),%L0\",
			     xoperands);
	    return \"\";
	  }
	else
	  {
	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[0] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %L0\", operands);
		else
		  output_asm_insn (\"mov %L1,%L0\", operands);
	      }
	    else
	      output_asm_insn (\"mov %L1,%L0\", operands);

	    if ((GET_CODE (operands[1]) == CONST_INT
		 || GET_CODE (operands[1]) == CONST_DOUBLE)
		&& val[1] == 0)
	      {
		if (REGNO_REG_CLASS (REGNO (operands[0])) == DATA_REGS)
		  output_asm_insn (\"clr %H0\", operands);
		else
		  output_asm_insn (\"mov %H1,%H0\", operands);
	      }
	    else if ((GET_CODE (operands[1]) == CONST_INT
		      || GET_CODE (operands[1]) == CONST_DOUBLE)
		     && val[0] == val[1])
	      output_asm_insn (\"mov %L0,%H0\", operands);
	    else
	      output_asm_insn (\"mov %H1,%H0\", operands);
	    return \"\";
	  }
    }
}"
  [(set_attr "cc" "none,none,clobber,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit,none_0hit")])
  


;; ----------------------------------------------------------------------
;; TEST INSTRUCTIONS
;; ----------------------------------------------------------------------

;; Go ahead and define tstsi so we can eliminate redundant tst insns
;; when we start trying to optimize this port.
(define_insn "tstsi"
  [(set (cc0) (match_operand:SI 0 "register_operand" "dax"))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn ""
  [(set (cc0) (zero_extend:SI (match_operand:QI 0 "memory_operand" "dx")))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])

(define_insn ""
  [(set (cc0) (zero_extend:SI (match_operand:HI 0 "memory_operand" "dx")))]
  ""
  "* return output_tst (operands[0], insn);"
  [(set_attr "cc" "set_znv")])


(define_insn "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "register_operand" "!*d*a*x,dax")
		 (match_operand:SI 1 "nonmemory_operand" "!*0,daxi")))]
  ""
  "@
  add 0,%0
  cmp %1,%0"
  [(set_attr "cc" "invert,compare")])

;; ----------------------------------------------------------------------
;; ADD INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "addsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "
{
  /* We can't add a variable amount directly to the stack pointer;
     so do so via a temporary register.  */
  if (operands[0] == stack_pointer_rtx
      && GET_CODE (operands[1]) != CONST_INT
      && GET_CODE (operands[2]) != CONST_INT)
   {
     rtx temp = gen_reg_rtx (SImode);
     emit_move_insn (temp, gen_rtx_PLUS (SImode, operands[1], operands[2]));
     emit_move_insn (operands[0], temp);
     DONE;
   }
}")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,ax,ax,dax,xy,!dax")
	(plus:SI (match_operand:SI 1 "register_operand" "%0,0,0,0,0,dax")
		 (match_operand:SI 2 "nonmemory_operand" "J,J,L,daxi,i,dax")))]
  ""
  "*
{
  switch (which_alternative)
    {
    case 0:
    case 1:
      return \"inc %0\";
    case 2:
      return \"inc4 %0\";
    case 3:
    case 4:
      return \"add %2,%0\";
    case 5:
      /* I'm not sure if this can happen or not.  Might as well be prepared
	 and generate the best possible code if it does happen.  */
      if (true_regnum (operands[0]) == true_regnum (operands[1]))
	return \"add %2,%0\";
      if (true_regnum (operands[0]) == true_regnum (operands[2]))
	return \"add %1,%0\";

      /* We have to copy one of the sources into the destination, then add
	 the other source to the destination.

	 Carefully select which source to copy to the destination; a naive
	 implementation will waste a byte when the source classes are different
	 and the destination is an address register.  Selecting the lowest
	 cost register copy will optimize this sequence.  */
      if (REGNO_REG_CLASS (true_regnum (operands[1]))
	  == REGNO_REG_CLASS (true_regnum (operands[0])))
	return \"mov %1,%0\;add %2,%0\";
      return \"mov %2,%0\;add %1,%0\";
    }
}"
  [(set_attr "cc" "set_zn,none_0hit,none_0hit,set_zn,none_0hit,set_zn")])

;; ----------------------------------------------------------------------
;; SUBTRACT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "subsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(minus:SI (match_operand:SI 1 "register_operand" "")
		  (match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dax")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
		  (match_operand:SI 2 "nonmemory_operand" "daxi")))]
  ""
  "sub %2,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "negsi2"
  [(set (match_operand:SI 0 "register_operand" "")
        (neg:SI (match_operand:SI 1 "register_operand" "")))]
  ""
  "
{
  rtx target = gen_reg_rtx (SImode);

  emit_move_insn (target, GEN_INT (0));
  emit_insn (gen_subsi3 (target, target, operands[1]));
  emit_move_insn (operands[0], target);
  DONE;
}")

;; ----------------------------------------------------------------------
;; MULTIPLY INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "mulsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(mult:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "register_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(mult:SI (match_operand:SI 1 "register_operand" "%0")
		 (match_operand:SI 2 "register_operand" "dx")))]
  ""
  "*
{
  if (TARGET_MULT_BUG)
    return \"nop\;nop\;mul %2,%0\";
  else
    return \"mul %2,%0\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn "udivmodsi4"
  [(set (match_operand:SI 0 "general_operand" "=dx")
	(udiv:SI (match_operand:SI 1 "general_operand" "0")
		 (match_operand:SI 2 "general_operand" "dx")))
   (set (match_operand:SI 3 "general_operand" "=&d")
	(umod:SI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  output_asm_insn (\"sub %3,%3\;mov %3,mdr\", operands);

  if (find_reg_note (insn, REG_UNUSED, operands[3]))
    return \"divu %2,%0\";
  else
    return \"divu %2,%0\;mov mdr,%3\";
}"
  [(set_attr "cc" "set_zn")])

(define_insn "divmodsi4"
  [(set (match_operand:SI 0 "general_operand" "=dx")
	(div:SI (match_operand:SI 1 "general_operand" "0")
		 (match_operand:SI 2 "general_operand" "dx")))
   (set (match_operand:SI 3 "general_operand" "=d")
	(mod:SI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  if (find_reg_note (insn, REG_UNUSED, operands[3]))
    return \"ext %0\;div %2,%0\";
  else
    return \"ext %0\;div %2,%0\;mov mdr,%3\";
}"
  [(set_attr "cc" "set_zn")])


;; ----------------------------------------------------------------------
;; AND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "andsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(and:SI (match_operand:SI 1 "register_operand" "")
		(match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx,dx")
	(and:SI (match_operand:SI 1 "register_operand" "%0,0")
		(match_operand:SI 2 "nonmemory_operand" "N,dxi")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xff)
    return \"extbu %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xffff)
    return \"exthu %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x7fffffff)
    return \"add %0,%0\;lsr 1,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x3fffffff)
    return \"asl2 %0\;lsr 2,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x1fffffff)
    return \"add %0,%0\;asl2 %0\;lsr 3,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0x0fffffff)
    return \"asl2 %0\;asl2 %0\;lsr 4,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffe)
    return \"lsr 1,%0\;add %0,%0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffffc)
    return \"lsr 2,%0\;asl2 %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff8)
    return \"lsr 3,%0\;add %0,%0\;asl2 %0\";
  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == 0xfffffff0)
    return \"lsr 4,%0\;asl2 %0\;asl2 %0\";
  return \"and %2,%0\";
}"
  [(set_attr "cc" "none_0hit,set_znv")])

;; ----------------------------------------------------------------------
;; OR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ior:SI (match_operand:SI 1 "register_operand" "")
		(match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(ior:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "dxi")))]
  ""
  "or %2,%0"
  [(set_attr "cc" "set_znv")])

;; ----------------------------------------------------------------------
;; XOR INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(xor:SI (match_operand:SI 1 "register_operand" "")
		(match_operand:SI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(xor:SI (match_operand:SI 1 "register_operand" "%0")
		(match_operand:SI 2 "nonmemory_operand" "dxi")))]
  ""
  "xor %2,%0"
  [(set_attr "cc" "set_znv")])

;; ----------------------------------------------------------------------
;; NOT INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "one_cmplsi2"
  [(set (match_operand:SI 0 "register_operand" "")
	(not:SI (match_operand:SI 1 "register_operand" "")))]
  ""
  "")
 
(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(not:SI (match_operand:SI 1 "register_operand" "0")))]
  ""
  "not %0"
  [(set_attr "cc" "set_znv")])

;; -----------------------------------------------------------------
;; BIT FIELDS
;; -----------------------------------------------------------------


;; These set/clear memory in byte sized chunks.
;;
;; They are no smaller/faster than loading the value into a register
;; and storing the register, but they don't need a scratch register
;; which may allow for better code generation.
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=R,d") (const_int 0))]
  ""
  "@
  bclr 255,%A0
  clr %0"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=R,d") (const_int -1))]
  ""
  "@
  bset 255,%A0
  mov -1,%0"
  [(set_attr "cc" "clobber,none_0hit")])

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "+R,d")
	(subreg:QI
	  (and:SI (subreg:SI (match_dup 0) 0)
		  (match_operand:SI 1 "const_int_operand" "i,i")) 0))]
  ""
  "@
  bclr %N1,%A0
  and %1,%0"
  [(set_attr "cc" "clobber,set_znv")])

(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "+R,d")
	(subreg:QI
	  (ior:SI (subreg:SI (match_dup 0) 0)
		  (match_operand:SI 1 "const_int_operand" "i,i")) 0))]
  ""
  "@
  bset %1,%A0
  or %1,%0"
  [(set_attr "cc" "clobber,set_znv")])

(define_insn ""
  [(set (cc0)
     (zero_extract:SI (match_operand:SI 0 "register_operand" "dx")
		      (match_operand 1 "const_int_operand" "")
		      (match_operand 2 "const_int_operand" "")))]
  ""
  "*
{
  int len = INTVAL (operands[1]);
  int bit = INTVAL (operands[2]);
  int mask = 0;
  rtx xoperands[2];

  while (len > 0)
    {
      mask |= (1 << bit);
      bit++;
      len--;
    }

  xoperands[0] = operands[0];
  xoperands[1] = GEN_INT (mask);
  output_asm_insn (\"btst %1,%0\", xoperands);
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (cc0)
     (zero_extract:SI (match_operand:QI 0 "general_operand" "R,dx")
		      (match_operand 1 "const_int_operand" "")
		      (match_operand 2 "const_int_operand" "")))]
  "mask_ok_for_mem_btst (INTVAL (operands[1]), INTVAL (operands[2]))"
  "*
{
  int len = INTVAL (operands[1]);
  int bit = INTVAL (operands[2]);
  int mask = 0;
  rtx xoperands[2];

  while (len > 0)
    {
      mask |= (1 << bit);
      bit++;
      len--;
    }

  /* If the source operand is not a reg (ie it is memory), then extract the
     bits from mask that we actually want to test.  Note that the mask will
     never cross a byte boundary.  */
  if (!REG_P (operands[0]))
    {
      if (mask & 0xff)
	mask = mask & 0xff;
      else if (mask & 0xff00)
	mask = (mask >> 8) & 0xff;
      else if (mask & 0xff0000)
	mask = (mask >> 16) & 0xff;
      else if (mask & 0xff000000)
	mask = (mask >> 24) & 0xff;
    }
  
  xoperands[0] = operands[0];
  xoperands[1] = GEN_INT (mask);
  if (GET_CODE (operands[0]) == REG)
    output_asm_insn (\"btst %1,%0\", xoperands);
  else
    output_asm_insn (\"btst %1,%A0\", xoperands);
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (cc0) (and:SI (match_operand:SI 0 "register_operand" "dx")
		      (match_operand:SI 1 "const_int_operand" "")))]
  ""
  "btst %1,%0"
  [(set_attr "cc" "clobber")])

(define_insn ""
  [(set (cc0)
     (and:SI
       (subreg:SI (match_operand:QI 0 "general_operand" "R,dx") 0)
       (match_operand:SI 1 "const_8bit_operand" "")))]
  ""
  "@
  btst %1,%A0
  btst %1,%0"
  [(set_attr "cc" "clobber")])


;; ----------------------------------------------------------------------
;; JUMP INSTRUCTIONS
;; ----------------------------------------------------------------------

;; Conditional jump instructions

(define_expand "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_expand "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "")

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
	  || GET_CODE (operands[1]) == GE
	  || GET_CODE (operands[1]) == LE
	  || GET_CODE (operands[1]) == LT))
    return 0;
  return \"b%b1 %0\";
}"
 [(set_attr "cc" "none")])

(define_insn ""
  [(set (pc)
	(if_then_else (match_operator 1 "comparison_operator"
				      [(cc0) (const_int 0)])
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if ((cc_status.flags & CC_OVERFLOW_UNUSABLE) != 0
      && (GET_CODE (operands[1]) == GT
	  || GET_CODE (operands[1]) == GE
	  || GET_CODE (operands[1]) == LE
	  || GET_CODE (operands[1]) == LT))
    return 0;
  return \"b%B1 %0\";
}"
 [(set_attr "cc" "none")])

;; Unconditional and other jump instructions.

(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jmp %l0"
 [(set_attr "cc" "none")])

(define_insn "indirect_jump"
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))]
  ""
  "jmp (%0)"
  [(set_attr "cc" "none")])

(define_insn "tablejump"
  [(set (pc) (match_operand:SI 0 "register_operand" "a"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp  (%0)"
  [(set_attr "cc" "none")])

;; Call subroutine with no return value.

(define_expand "call"
  [(call (match_operand:QI 0 "general_operand" "")
	 (match_operand:SI 1 "general_operand" ""))]
  ""
  "
{
  if (! call_address_operand (XEXP (operands[0], 0)))
    XEXP (operands[0], 0) = force_reg (SImode, XEXP (operands[0], 0));
  emit_call_insn (gen_call_internal (XEXP (operands[0], 0), operands[1]));
  DONE;
}")

(define_insn "call_internal"
  [(call (mem:QI (match_operand:SI 0 "call_address_operand" "aS"))
	 (match_operand:SI 1 "general_operand" "g"))]
  ""
  "*
{
  if (REG_P (operands[0]))
    return \"calls %C0\";
  else
    return \"call %C0,[],0\";
}"
  [(set_attr "cc" "clobber")])

;; Call subroutine, returning value in operand 0
;; (which must be a hard register).

(define_expand "call_value"
  [(set (match_operand 0 "" "")
	(call (match_operand:QI 1 "general_operand" "")
	      (match_operand:SI 2 "general_operand" "")))]
  ""
  "
{
  if (! call_address_operand (XEXP (operands[1], 0)))
    XEXP (operands[1], 0) = force_reg (SImode, XEXP (operands[1], 0));
  emit_call_insn (gen_call_value_internal (operands[0],
					   XEXP (operands[1], 0),
					   operands[2]));
  DONE;
}")

(define_insn "call_value_internal"
  [(set (match_operand 0 "" "=dax")
	(call (mem:QI (match_operand:SI 1 "call_address_operand" "aS"))
	      (match_operand:SI 2 "general_operand" "g")))]
  ""
  "*
{
  if (REG_P (operands[1]))
    return \"calls %C1\";
  else
    return \"call %C1,[],0\";
}"
  [(set_attr "cc" "clobber")])

(define_expand "untyped_call"
  [(parallel [(call (match_operand 0 "" "")
                    (const_int 0))
              (match_operand 1 "" "")
              (match_operand 2 "" "")])]
  ""
  "
{
  int i;

  emit_call_insn (gen_call (operands[0], const0_rtx));

  for (i = 0; i < XVECLEN (operands[2], 0); i++)
    {
      rtx set = XVECEXP (operands[2], 0, i);
      emit_move_insn (SET_DEST (set), SET_SRC (set));
    }
  DONE;
}")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop"
  [(set_attr "cc" "none")])

;; ----------------------------------------------------------------------
;; EXTEND INSTRUCTIONS
;; ----------------------------------------------------------------------

(define_expand "zero_extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(zero_extend:SI
	 (match_operand:QI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=dx,dx,dx")
	(zero_extend:SI
	 (match_operand:QI 1 "general_operand" "0,d,m")))]
  ""
  "@
  extbu %0
  mov %1,%0\;extbu %0
  movbu %1,%0"
  [(set_attr "cc" "none_0hit")])

(define_expand "zero_extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(zero_extend:SI
	 (match_operand:HI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=dx,dx,dx")
	(zero_extend:SI
	 (match_operand:HI 1 "general_operand" "0,dx,m")))]
  ""
  "@
  exthu %0
  mov %1,%0\;exthu %0
  movhu %1,%0"
  [(set_attr "cc" "none_0hit")])

;;- sign extension instructions

(define_expand "extendqisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(sign_extend:SI
	 (match_operand:QI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=dx,dx")
	(sign_extend:SI
	 (match_operand:QI 1 "general_operand" "0,dx")))]
  ""
  "@
  extb %0
  mov %1,%0\;extb %0"
  [(set_attr "cc" "none_0hit")])

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "")
	(sign_extend:SI
	 (match_operand:HI 1 "general_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "general_operand" "=dx,dx")
	(sign_extend:SI
	 (match_operand:HI 1 "general_operand" "0,dx")))]
  ""
  "@
  exth %0
  mov %1,%0\;exth %0"
  [(set_attr "cc" "none_0hit")])

;; ----------------------------------------------------------------------
;; SHIFTS
;; ----------------------------------------------------------------------

(define_expand "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashift:SI
	 (match_operand:SI 1 "register_operand" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dax,dx,dx,dx,dx")
	(ashift:SI
	 (match_operand:SI 1 "register_operand" "0,0,0,0,0")
	 (match_operand:QI 2 "nonmemory_operand" "J,K,M,L,dxi")))]
  ""
  "@
  add %0,%0
  asl2 %0
  asl2 %0\;add %0,%0
  asl2 %0\;asl2 %0
  asl %S2,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(lshiftrt:SI
	 (match_operand:SI 1 "register_operand" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(lshiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "dxi")))]
  ""
  "lsr %S2,%0"
  [(set_attr "cc" "set_zn")])

(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "")
	(ashiftrt:SI
	 (match_operand:SI 1 "register_operand" "")
	 (match_operand:QI 2 "nonmemory_operand" "")))]
  ""
  "")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=dx")
	(ashiftrt:SI
	 (match_operand:SI 1 "register_operand" "0")
	 (match_operand:QI 2 "nonmemory_operand" "dxi")))]
  ""
  "asr %S2,%0"
  [(set_attr "cc" "set_zn")])

;; ----------------------------------------------------------------------
;; FP INSTRUCTIONS
;; ----------------------------------------------------------------------
;;
;; The mn103 series does not have floating point instructions, but since
;; FP values are held in integer regs, we can clear the high bit easily
;; which gives us an efficient inline floating point absolute value.
;;
;; Similarly for negation of a FP value.
;;

(define_expand "absdf2"
  [(set (match_operand:DF 0 "register_operand" "")
        (abs:DF (match_operand:DF 1 "register_operand" "")))]
  ""
  "
{
  rtx target, result, insns;

  start_sequence ();
  target = operand_subword (operands[0], 1, 1, DFmode);
  result = expand_binop (SImode, and_optab,
			 operand_subword_force (operands[1], 1, DFmode),
			 GEN_INT(0x7fffffff), target, 0, OPTAB_WIDEN);

  if (result == 0)
    abort ();

  if (result != target)
    emit_move_insn (result, target);

  emit_move_insn (operand_subword (operands[0], 0, 1, DFmode),
		  operand_subword_force (operands[1], 0, DFmode));

  insns = get_insns ();
  end_sequence ();

  emit_no_conflict_block (insns, operands[0], operands[1], 0, 0);
  DONE;
}")

(define_expand "abssf2"
  [(set (match_operand:SF 0 "register_operand" "")
        (abs:SF (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{
  rtx result;
  rtx target;

  target = operand_subword_force (operands[0], 0, SFmode);
  result = expand_binop (SImode, and_optab,
			 operand_subword_force (operands[1], 0, SFmode),
			 GEN_INT(0x7fffffff), target, 0, OPTAB_WIDEN);
  if (result == 0)
    abort ();

  if (result != target)
    emit_move_insn (result, target);

  /* Make a place for REG_EQUAL.  */
  emit_move_insn (operands[0], operands[0]);
  DONE;
}")


(define_expand "negdf2"
  [(set (match_operand:DF 0 "register_operand" "")
        (neg:DF (match_operand:DF 1 "register_operand" "")))]
  ""
  "
{
  rtx target, result, insns;

  start_sequence ();
  target = operand_subword (operands[0], 1, 1, DFmode);
  result = expand_binop (SImode, xor_optab,
			 operand_subword_force (operands[1], 1, DFmode),
			 GEN_INT(0x80000000), target, 0, OPTAB_WIDEN);

  if (result == 0)
    abort ();

  if (result != target)
    emit_move_insn (result, target);

  emit_move_insn (operand_subword (operands[0], 0, 1, DFmode),
		  operand_subword_force (operands[1], 0, DFmode));

  insns = get_insns ();
  end_sequence ();

  emit_no_conflict_block (insns, operands[0], operands[1], 0, 0);
  DONE;
}")

(define_expand "negsf2"
  [(set (match_operand:SF 0 "register_operand" "")
        (neg:SF (match_operand:SF 1 "register_operand" "")))]
  ""
  "
{
  rtx result;
  rtx target;

  target = operand_subword_force (operands[0], 0, SFmode);
  result = expand_binop (SImode, xor_optab,
			 operand_subword_force (operands[1], 0, SFmode),
			 GEN_INT(0x80000000), target, 0, OPTAB_WIDEN);
  if (result == 0)
    abort ();

  if (result != target)
    emit_move_insn (result, target);

  /* Make a place for REG_EQUAL.  */
  emit_move_insn (operands[0], operands[0]);
  DONE;
}")


;; ----------------------------------------------------------------------
;; PROLOGUE/EPILOGUE
;; ----------------------------------------------------------------------
(define_expand "prologue"
  [(const_int 0)]
  ""
  "expand_prologue (); DONE;")

(define_expand "epilogue"
  [(return)]
  ""
  "
{
  expand_epilogue ();
  DONE;
}")

(define_insn "return_internal"
  [(const_int 2)
   (return)]
  ""
  "rets"
  [(set_attr "cc" "clobber")])

;; This insn restores the callee saved registers and does a return, it
;; can also deallocate stack space.
(define_insn "return_internal_regs"
  [(const_int 0)
   (match_operand:SI 0  "const_int_operand" "i")
   (return)]
  ""
  "*
{
  int i, need_comma;
  int d2, d3, a2, a3;

  need_comma = 0;
  fputs (\"\\tret [\", asm_out_file);
  if (regs_ever_live[2])
    {
      fputs (\"d2\", asm_out_file);
      need_comma = 1;
    }
  if (regs_ever_live[3])
    {
      if (need_comma)
	fputc (',', asm_out_file); 
      fputs (\"d3\", asm_out_file);
      need_comma = 1;
    }
  if (regs_ever_live[6])
    {
      if (need_comma)
	fputc (',', asm_out_file); 
      fputs (\"a2\", asm_out_file);
      need_comma = 1;
    }
  if (regs_ever_live[7])
    {
      if (need_comma)
	fputc (',', asm_out_file); 
      fputs (\"a3\", asm_out_file);
      need_comma = 1;
    }
  fprintf (asm_out_file, \"],%d\\n\", INTVAL (operands[0]));
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn "store_movm"
  [(const_int 1)]
  ""
  "*
{
  int i, need_comma;
  int d2, d3, a2, a3;

  need_comma = 0;
  fputs (\"\\tmovm [\", asm_out_file);
  if (regs_ever_live[2])
    {
      fputs (\"d2\", asm_out_file);
      need_comma = 1;
    }
  if (regs_ever_live[3])
    {
      if (need_comma)
	fputc (',', asm_out_file); 
      fputs (\"d3\", asm_out_file);
      need_comma = 1;
    }
  if (regs_ever_live[6])
    {
      if (need_comma)
	fputc (',', asm_out_file); 
      fputs (\"a2\", asm_out_file);
      need_comma = 1;
    }
  if (regs_ever_live[7])
    {
      if (need_comma)
	fputc (',', asm_out_file); 
      fputs (\"a3\", asm_out_file);
      need_comma = 1;
    }
  fputs (\"],(sp)\\n\", asm_out_file);
  return \"\";
}"
  [(set_attr "cc" "clobber")])

(define_insn "return"
  [(return)]
  "can_use_return_insn ()"
  "*
{
  rtx next = next_active_insn (insn);

  if (next
      && GET_CODE (next) == JUMP_INSN
      && GET_CODE (PATTERN (next)) == RETURN)
    return \"\";
  else
    return \"rets\";
}"
  [(set_attr "cc" "clobber")])

;; Try to combine consecutive updates of the stack pointer (or any
;; other register for that matter).
(define_peephole
  [(set (match_operand:SI 0 "register_operand" "=dxay")
	(plus:SI (match_dup 0)
		 (match_operand 1 "const_int_operand" "")))
   (set (match_dup 0)
	(plus:SI (match_dup 0)
		 (match_operand 2 "const_int_operand" "")))]
  ""
  "*
{
  operands[1] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[1]));
  return \"add %1,%0\";
}"
  [(set_attr "cc" "clobber")])

;;
;; We had patterns to check eq/ne, but the they don't work because
;; 0x80000000 + 0x80000000 = 0x0 with a carry out.
;;
;; The Z flag and C flag would be set, and we have no way to
;; check for the Z flag set and C flag clear.
;;
;; This will work on the mn10200 because we can check the ZX flag
;; if the comparison is in HImode.
(define_peephole
  [(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcc %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (match_operand 1 "" "")
			   (pc)))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcs %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
   (set (pc) (if_then_else (ge (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcs %1"
  [(set_attr "cc" "clobber")])

(define_peephole
  [(set (cc0) (match_operand:SI 0 "register_operand" "dx"))
   (set (pc) (if_then_else (lt (cc0) (const_int 0))
			   (pc)
			   (match_operand 1 "" "")))]
  "dead_or_set_p (ins1, operands[0]) && REG_OK_FOR_INDEX_P (operands[0])"
  "add %0,%0\;bcc %1"
  [(set_attr "cc" "clobber")])

