;;- Machine description file for Motorola 68HC11 and 68HC12.
;;- Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
;;- Contributed by Stephane Carrez (stcarrez@nerim.fr)

;; 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.

;; Note:
;;   A first 68HC11 port was made by Otto Lind (otto@coactive.com)
;;   on gcc 2.6.3.  I have used it as a starting point for this port.
;;   However, this new port is a complete re-write.  Its internal
;;   design is completely different.  The generated code is not
;;   compatible with the gcc 2.6.3 port.
;;
;;   The gcc 2.6.3 port is available at:
;;
;;   ftp.unina.it/pub/electronics/motorola/68hc11/gcc/gcc-6811-fsf.tar.gz
;;

;;- Instruction patterns.  When multiple patterns apply,
;;- the first one in the file is chosen.
;;-
;;- See file "rtl.def" for documentation on define_insn, match_*, et. al.
;;-
;;- cpp macro #define NOTICE_UPDATE_CC in file tm.h handles condition code
;;- updates for most instructions.

;;
;; The following constraints are used:
;;
;; Single pair registers:
;; a    register 'a'			 8-bit
;; b    register 'b'			 8-bit
;; d    register 'd'			16-bit
;; t    pseudo soft register 'TMP'      16-bit
;; v    register 'd' for 68hc11,	16-bit
;;      NO_REG for 68hc12
;;      (used for scratch register)
;; w    register 'sp'			16-bit 
;; x    register 'x'			16-bit
;; y    register 'y'			16-bit
;; z    register 'z'			16-bit  (fake r for 68HC11 and 68HC12)
;; D    register 'd+x'			32-bit 
;;
;; Group of registers:
;; q    register 'a' or 'b' or 'd'	 8-bit
;; u    pseudo soft register		16-bit
;; A    register 'x', 'y', 'z'		16-bit
;; B    register 'x', 'y'               16-bit
;; h	register 'd', 'x', 'y', 'z'	16-bit
;;
;; Other constraints:
;;
;; Q    an operand which is in memory but whose address is constant
;;      (ie, a (MEM (SYMBOL_REF x))).  This constraint is used by
;;      bset/bclr instructions together with linker relaxation.  The
;;      operand can be translated to a page0 addressing mode if the
;;      symbol address is in page0 (0..255).
;;
;; R    an operand which is in memory and whose address is expressed
;;      with 68HC11/68HC12 indexed addressing mode.  In general this
;;      is any valid (MEM) except a (MEM (SYMBOL_REF x)).
;;
;; U    an operand which is in memory and if it uses the 68HC12 indexed
;;      addressing mode, the offset is in the range -16..+15.  This is
;;      used by 68HC12 movb/movw instructions since they do not accept
;;      the full 16-bit offset range (as other insn do).
;;
;;
;; Immediate integer operand constraints:
;;   `L' is for range -65536 to 65536
;;   `M' is for values whose 16-bit low part is 0
;;   'N' is for +1 or -1.
;;   'O' is for 16 (for rotate using swap).
;;   'P' is for range -8 to 2 (used by addhi_sp)
;;
;; In many cases, it's not possible to use the 'g' or 'r' constraints.
;;
;; Operands modifiers:
;;
;;     %b	Get the low part of the operand (to obtain a QImode)
;;		This modified must always be used for QImode operations
;;		because a correction must be applied when the operand
;;		is a soft register (ex: *ZD1). Otherwise, we generate
;;		*ZD1 and this is the high part of the register. For other
;;		kinds of operands, if the operand is already QImode, no
;;		additional correction is made.
;;     %h	Get the high part of the operand (to obtain a QImode)
;;     %t	Represents the temporary/scratch register *_.tmp
;;		The scratch register is used in some cases when GCC puts
;;		some values in bad registers. 
;;
;; 32/64-bit Patterns:
;;     The 68HC11 does not support 32/64-bit operations.  Most of the
;;     32/64-bit patterns are defined to split the instruction in
;;     16-bits patterns.  Providing split patterns generates better code
;;     than letting GCC implement the 32/64-bit operation itself.
;;
;;
;; Notes:
;;
;; o For iorqi3, andqi3, xorqi3 patterns, we must accept the 'A' constraint
;;   otherwise some insn are not satisfied.
;;
;; o Split patterns that create a swap_areg pattern (xgdx or xgdy) must
;;   be valid only when z_replacement_completed == 2 because once these
;;   swap instructions are generated, a flow/cse pass fails to handle
;;   them correctly (it would treat the X, Y or D register as dead sometimes).
;;
;; o Some split pattern generate instructions that operate on 'a' or 'b'
;;   register directly (high part and low part of D respectively).
;;   Such split pattern must also be valid when z_replacement_completed == 2
;;   because flow/cse is not aware that D is composed of {a, b}.
;;
;; o Split patterns that generate a (mem:QI (symbol_reg _.dx)) to access
;;   the high part of a soft register must be expanded after z_replacement
;;   pass.
;;
;;---------------------------------------------------------------------------
;; Constants

(define_constants [
   ;; Register numbers
   (X_REGNUM	    0)		; Index X register
   (D_REGNUM	    1)		; Data register
   (Y_REGNUM        2)		; Index Y register
   (SP_REGNUM       3)          ; Stack pointer
   (PC_REGNUM	    4)		; Program counter
   (A_REGNUM        5)		; A (high part of D)
   (B_REGNUM        6)		; B (low part of D)
   (CC_REGNUM       7)		; Condition code register
   (SOFT_Z_REGNUM  11)          ; Z soft register
])

;;--------------------------------------------------------------------
;;-  Test
;;--------------------------------------------------------------------
;;
;; The test and compare insn must not accept a memory operand with
;; an auto-inc mode.  If we do this, the reload can emit move insns
;; after the test or compare.  Such move will set the flags and therefore
;; break the comparison.  This can happen if the auto-inc register
;; does not happen to be a hard register (ie, reloading occurs).
;; An offsetable memory operand should be ok.  The 'tst_operand' and
;; 'cmp_operand' predicates take care of this rule.
;;
(define_expand "tstsi"
  [(set (cc0)
	(match_operand:SI 0 "tst_operand" ""))]
  ""
  "
{
  m68hc11_compare_op0 = operands[0];
  m68hc11_compare_op1 = const0_rtx;
  DONE;
}")

(define_expand "tsthi"
  [(set (cc0)
	(match_operand:HI 0 "tst_operand" ""))]
  ""
  "
{
  m68hc11_compare_op0 = operands[0];
  m68hc11_compare_op1 = const0_rtx;
  DONE;
}")

(define_insn "tsthi_1"
  [(set (cc0)
	(match_operand:HI 0 "tst_operand" "dx,*y"))]
  ""
  "*
{
   if (D_REG_P (operands[0]) && !TARGET_M6812)
     return \"std\\t%t0\";
   else
     return \"cp%0\\t#0\";
}")

(define_expand "tstqi"
  [(set (cc0)
	(match_operand:QI 0 "tst_operand" ""))]
  ""
  "
{
  m68hc11_compare_op0 = operands[0];
  m68hc11_compare_op1 = const0_rtx;
  DONE;
}")

;;
;; Split pattern for (tst:QI) on an address register.
;;
(define_split
  [(set (cc0)
	(match_operand:QI 0 "hard_addr_reg_operand" ""))]
  "z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
  [(parallel [(set (reg:HI D_REGNUM) (match_dup 1))
	      (set (match_dup 1) (reg:HI D_REGNUM))])
   (set (cc0) (reg:QI D_REGNUM))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 1))
	      (set (match_dup 1) (reg:HI D_REGNUM))])]
  "operands[1] = gen_rtx (REG, HImode, REGNO (operands[0]));")

(define_insn "tstqi_1"
  [(set (cc0)
	(match_operand:QI 0 "tst_operand" "m,d,*A,!u"))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  else if (D_REG_P (operands[0]))
    return \"tstb\";

  else if (dead_register_here (insn, d_reg))
    return \"ldab\\t%b0\";

  else
    return \"tst\\t%b0\";
}")

;;
;; tstqi_z_used, cmpqi_z_used and cmphi_z_used are patterns generated 
;; during the Z register replacement.  They are used when an operand
;; uses the Z register as an index register (ie, (MEM:QI (REG:HI Z))).
;; In that case, we have to preserve the values of the replacement
;; register (as well as the CC0 since the insns are compare insns).
;; To do this, the replacement register is pushed on the stack and
;; restored after the real compare.  A pattern+split is defined to
;; avoid problems with the flow+cse register pass which are made
;; after Z register replacement.
;;
(define_insn "tstqi_z_used"
  [(set (cc0)
	(match_operand:QI 0 "tst_operand" "m"))
   (use (match_operand:HI 1 "hard_reg_operand" "dxy"))
   (use (reg:HI 11))]
  ""
  "#")

(define_split /* "tstqi_z_used" */
  [(set (cc0)
	(match_operand:QI 0 "tst_operand" ""))
   (use (match_operand:HI 1 "hard_reg_operand" ""))
   (use (reg:HI SOFT_Z_REGNUM))]
  "z_replacement_completed == 2"
  [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 1))
   (set (match_dup 1) (match_dup 2))
   (set (cc0) (match_dup 0))
   (set (match_dup 1) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
  "operands[2] = gen_rtx (REG, HImode, SOFT_Z_REGNUM);")


;;--------------------------------------------------------------------
;;- Compare
;;--------------------------------------------------------------------

(define_expand "cmpsi"
  [(set (cc0)
	(compare (match_operand:SI 0 "tst_operand" "")
		 (match_operand:SI 1 "cmp_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[0] = force_reg (SImode, operands[0]);

  m68hc11_compare_op0 = operands[0];
  m68hc11_compare_op1 = operands[1];
  DONE;
}")

;;
;; Comparison of a hard register with another one is provided because
;; it helps GCC to avoid to spill a pseudo hard register.
;; We use a temporary in page 0, this is equivalent to a pseudo hard reg.
;; (except that we loose the information that the value is saved in it).
;;
;; The split pattern transforms the comparison into a save of one hard
;; register and a comparison with the temporary.
;;
(define_split
  [(set (cc0)
	(compare (match_operand:HI 0 "hard_reg_operand" "")
		 (match_operand:HI 1 "hard_reg_operand" "")))]
  "reload_completed"
  [(set (match_dup 2) (match_dup 1))
   (set (cc0)
        (compare (match_dup 0) (match_dup 2)))]
  "operands[2] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);")

(define_expand "cmphi"
  [(set (cc0)
	(compare (match_operand:HI 0 "tst_operand" "")
		 (match_operand:HI 1 "cmp_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[0] = force_reg (HImode, operands[0]);

  m68hc11_compare_op0 = operands[0];
  m68hc11_compare_op1 = operands[1];
  DONE;
}")

(define_insn "cmphi_1_hc12"
  [(set (cc0)
	(compare (match_operand:HI 0 "tst_operand" 
				"d,?xy,xyd,?xy,d,m,!u,dxy,dxy")
		 (match_operand:HI 1 "cmp_operand"
				"i,i,!u,m,m,dxy,dxy,?*d*A,!*w")))]
  "TARGET_M6812"
  "*
{
  if (H_REG_P (operands[1]) && !H_REG_P (operands[0]))
    {
      cc_status.flags |= CC_REVERSED;
      return \"cp%1\\t%0\";
    }
  else if (H_REG_P (operands[1]))
    return \"#\";
  else
    return \"cp%0\\t%1\";
}")

(define_insn "cmphi_1_hc11"
  [(set (cc0)
	(compare (match_operand:HI 0 "tst_operand" 
				"dx,y,xyd,?xy,d,m,!u,dxy,dxy")
		 (match_operand:HI 1 "cmp_operand"
				"i,i,!u,m,m,dxy,dxy,?*d*A,!*w")))]
  "TARGET_M6811"
  "*
{
  if (H_REG_P (operands[1]) && !H_REG_P (operands[0]))
    {
      cc_status.flags |= CC_REVERSED;
      return \"cp%1\\t%0\";
    }
  else if (H_REG_P (operands[1]))
    return \"#\";
  else
    return \"cp%0\\t%1\";
}")

(define_insn "cmphi_z_used"
  [(set (cc0)
	(compare (match_operand:HI 0 "tst_operand" "dxy,m")
		 (match_operand:HI 1 "cmp_operand" "m,dxy")))
   (use (match_operand:HI 2 "hard_reg_operand" "dxy,dxy"))
   (use (reg:HI SOFT_Z_REGNUM))]
  ""
  "#")
  
(define_split /* "cmphi_z_used" */
  [(set (cc0)
	(compare (match_operand:HI 0 "tst_operand" "")
		 (match_operand:HI 1 "cmp_operand" "")))
   (use (match_operand:HI 2 "hard_reg_operand" ""))
   (use (reg:HI SOFT_Z_REGNUM))]
  "z_replacement_completed == 2"
  [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
   (set (match_dup 2) (match_dup 3))
   (set (cc0) (compare (match_dup 0) (match_dup 1)))
   (set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
  "operands[3] = gen_rtx (REG, HImode, SOFT_Z_REGNUM);")

;;
;; 8-bit comparison with address register.
;; There is no such comparison instruction, we have to temporarily switch
;; the address register and the D register and do the comparison with D.
;; The xgdx and xgdy instructions preserve the flags.
;;
(define_split
  [(set (cc0)
	(compare (match_operand:QI 0 "hard_addr_reg_operand" "")
		 (match_operand:QI 1 "cmp_operand" "")))]
  "z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
  [(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
              (set (match_dup 3) (reg:HI D_REGNUM))])
   (set (cc0)
        (compare (reg:QI D_REGNUM) (match_dup 1)))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
              (set (match_dup 3) (reg:HI D_REGNUM))])]
  "operands[3] = gen_rtx (REG, HImode, REGNO (operands[0]));")

(define_split
  [(set (cc0)
	(compare (match_operand:QI 0 "hard_reg_operand" "")
		 (match_operand:QI 1 "hard_reg_operand" "")))]
  "reload_completed"
  [(set (match_dup 3) (match_dup 4))
   (set (cc0)
        (compare (match_dup 0) (match_dup 2)))]
  "operands[2] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);
   operands[3] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
   operands[4] = gen_rtx (REG, HImode, REGNO (operands[1]));")

(define_expand "cmpqi"
  [(set (cc0)
	(compare (match_operand:QI 0 "tst_operand" "")
		 (match_operand:QI 1 "cmp_operand" "")))]
  ""
  "
{
  if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) == MEM)
    operands[0] = force_reg (QImode, operands[0]);

  m68hc11_compare_op0 = operands[0];
  m68hc11_compare_op1 = operands[1];
  DONE;
}")

(define_insn "bitcmpqi"
  [(set (cc0)
	(and:QI (match_operand:QI 0 "tst_operand" "d,d,d,m,!u")
	        (match_operand:QI 1 "cmp_operand" "im,*B,u,d,d")))]
  ""
  "@
   bitb\\t%b1
   #
   bitb\\t%b1
   bitb\\t%b0
   bitb\\t%b0")

(define_split /* "bitcmpqi" */
  [(set (cc0)
	(and:QI (match_operand:QI 0 "tst_operand" "")
		(match_operand:QI 1 "hard_addr_reg_operand" "")))]
  "z_replacement_completed == 2"
  [(set (match_dup 3) (match_dup 2))
   (set (cc0) (and:QI (match_dup 0) (match_dup 4)))]
  "operands[2] = gen_rtx (REG, HImode, REGNO (operands[1]));
   operands[3] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
   operands[4] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);")

(define_insn "bitcmpqi_z_used"
  [(set (cc0)
	(and:QI (match_operand:QI 0 "tst_operand" "d,m")
		(match_operand:QI 1 "cmp_operand" "m,d")))
   (use (match_operand:HI 2 "hard_reg_operand" "xy,xy"))
   (use (reg:HI SOFT_Z_REGNUM))]
  ""
  "#")
  
(define_split /* "bitcmpqi_z_used" */
  [(set (cc0)
	(and:QI (match_operand:QI 0 "tst_operand" "")
		(match_operand:QI 1 "cmp_operand" "")))
   (use (match_operand:HI 2 "hard_reg_operand" ""))
   (use (reg:HI SOFT_Z_REGNUM))]
  "z_replacement_completed == 2"
  [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
   (set (match_dup 2) (match_dup 3))
   (set (cc0) (and:QI (match_dup 0) (match_dup 1)))
   (set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
  "operands[3] = gen_rtx (REG, HImode, SOFT_Z_REGNUM);")

(define_insn "bitcmphi"
  [(set (cc0)
	(and:HI (match_operand:HI 0 "tst_operand" "d")
	        (match_operand:HI 1 "const_int_operand" "i")))]
  "(INTVAL (operands[1]) & 0x0ff) == 0
   || (INTVAL (operands[1]) & 0x0ff00) == 0"
  "*
{
   if ((INTVAL (operands[1]) & 0x0ff) == 0)
     return \"bita\\t%h1\";
   else
     return \"bitb\\t%1\";
}")

(define_insn "bitcmpqi_12"
  [(set (cc0)
	(zero_extract (match_operand:HI 0 "tst_operand" "d")
	              (match_operand:HI 1 "const_int_operand" "i")
		      (match_operand:HI 2 "const_int_operand" "i")))]
  "(unsigned) (INTVAL (operands[2]) + INTVAL (operands[1])) <= 8
   || (((unsigned) (INTVAL (operands[2]) + INTVAL (operands[1])) <= 16)
       && (unsigned) INTVAL (operands[2]) >= 8)"
  "*
{
   rtx ops[1];
   int mask;
   int startpos = INTVAL (operands[2]);
   int bitsize = INTVAL (operands[1]);

   if (startpos >= 8)
     {
       startpos -= 8;
       mask = (1 << (startpos + bitsize)) - 1;
       mask &= ~((1 << startpos) - 1);

       ops[0] = GEN_INT (mask);
       output_asm_insn (\"bita\\t%0\", ops);
     }
   else
     {
       mask = (1 << (startpos + bitsize)) - 1;
       mask &= ~((1 << startpos) - 1);

       ops[0] = GEN_INT (mask);
       output_asm_insn (\"bitb\\t%0\", ops);
     }
   return \"\";
}")

(define_insn "cmpqi_1"
  [(set (cc0)
	(compare (match_operand:QI 0 "tst_operand" "d,m,d,!u,*B,d*B")
		 (match_operand:QI 1 "cmp_operand" "im,d,!u,d,dim*A,*u")))]
  ""
  "*
{
   if (A_REG_P (operands[0]) || A_REG_P (operands[1]))
     {
        return \"#\";
     }
   else if (D_REG_P (operands[0]))
     {
        return \"cmpb\\t%b1\";
     }
   cc_status.flags |= CC_REVERSED;
   return \"cmpb\\t%b0\";
}")

(define_insn "cmpqi_z_used"
  [(set (cc0)
	(compare (match_operand:QI 0 "tst_operand" "dxy,m")
		 (match_operand:QI 1 "cmp_operand" "m,dxy")))
   (use (match_operand:HI 2 "hard_reg_operand" "dxy,dxy"))
   (use (reg:HI SOFT_Z_REGNUM))]
  ""
  "#")
  
(define_split /* cmpqi_z_used */
  [(set (cc0)
	(compare (match_operand:QI 0 "tst_operand" "")
		 (match_operand:QI 1 "cmp_operand" "")))
   (use (match_operand:HI 2 "hard_reg_operand" ""))
   (use (reg:HI SOFT_Z_REGNUM))]
  "z_replacement_completed == 2"
  [(set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))
   (set (match_dup 2) (match_dup 3))
   (set (cc0) (compare (match_dup 0) (match_dup 1)))
   (set (match_dup 2) (mem:HI (post_inc:HI (reg:HI SP_REGNUM))))]
  "operands[3] = gen_rtx (REG, HImode, SOFT_Z_REGNUM);")

;;--------------------------------------------------------------------
;;-  Move strict_low_part
;;--------------------------------------------------------------------
;;
;; The (strict_low_part ...) patterns are replaced by normal (set) patterns.
;; The replacement must be made at the very end because we loose the
;; (strict_low_part ...) information.  This is correct for our machine
;; description but not for GCC optimization passes.
;;
(define_insn "movstrictsi"
  [(set (strict_low_part (match_operand:SI 0 "non_push_operand" "+um,D,D"))
	(match_operand:SI 1 "general_operand" "D,Dim,uD"))]
  ""
  "#")

(define_split
  [(set (strict_low_part (match_operand:SI 0 "non_push_operand" ""))
	(match_operand:SI 1 "general_operand" ""))]
  "z_replacement_completed == 2"
  [(set (match_dup 0) (match_dup 1))]
  "")

(define_insn "movstricthi"
  [(set (strict_low_part (match_operand:HI 0 "non_push_operand" "+um,dA,dA"))
	(match_operand:HI 1 "general_operand" "dA,dAim,u"))]
  ""
  "#")

(define_split
  [(set (strict_low_part (match_operand:HI 0 "non_push_operand" ""))
	(match_operand:HI 1 "general_operand" ""))]
  "z_replacement_completed == 2"
  [(set (match_dup 0) (match_dup 1))]
  "")

(define_insn "movstrictqi"
  [(set (strict_low_part (match_operand:QI 0 "non_push_operand" "+mu,!dA"))
	(match_operand:QI 1 "general_operand" "d,imudA"))]
  ""
  "#")

(define_split
  [(set (strict_low_part (match_operand:QI 0 "non_push_operand" ""))
	(match_operand:QI 1 "general_operand" ""))]
  "z_replacement_completed == 2"
  [(set (match_dup 0) (match_dup 1))]
  "")

;;--------------------------------------------------------------------
;;- 64-bit Move Operations.
;; The movdi and movdf patterns are identical except for the mode.
;; They are also very similar to those for movsi and movsf.
;;
;; For 68HC11, we need a scratch register (either D, X, Y) 
;; because there is no memory->memory moves.  It must be defined with
;; earlyclobber (&) so that it does not appear in the source or destination 
;; address.  Providing patterns for movdi/movdf allows GCC to generate
;; better code.  [Until now, the scratch register is limited to D because
;; otherwise we can run out of registers in the A_REGS class for reload].
;;
;; For 68HC12, the scratch register is not necessary.  To use the same
;; pattern and same split, we use the 'v' constraint.  This tells the
;; reload to use the _.tmp register (which is not used at all).
;; The insn will be split in one or several memory moves (movw).
;; [SCz: this does not work ?? So, I switched temporary to 'd' reg]
;;--------------------------------------------------------------------
(define_expand "movdi"
  [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
		   (match_operand:DI 1 "general_operand" ""))
	      (clobber (match_scratch:HI 2 ""))])]
  ""
  "
  /* For push/pop, emit a REG_INC note to make sure the reload
     inheritance and reload CSE pass notice the change of the stack
     pointer.  */
  if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
    {
      rtx insn;

      insn = emit_insn (gen_movdi_internal (operands[0], operands[1]));
      REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
					  stack_pointer_rtx,
					  REG_NOTES (insn));
      DONE;
    }
")

(define_insn "movdi_internal"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=ou,U,!u,U,m,m,!u")
	(match_operand:DI 1 "general_operand" "K,iU,iU,!u,mi,!u,!mu"))
   (clobber (match_scratch:HI 2 "=X,&d,&d,&d,&d,&d,&d"))]
  ""
  "#")

(define_split
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(match_operand:DI 1 "general_operand" ""))
   (clobber (match_scratch:HI 2 ""))]
  "reload_completed"
  [(const_int 0)]
  "m68hc11_split_move (operands[0], operands[1], operands[2]);
   DONE;")

(define_expand "movdf"
  [(parallel [(set (match_operand:DF 0 "nonimmediate_operand" "")
		   (match_operand:DF 1 "general_operand" ""))
	      (clobber (match_scratch:HI 2 ""))])]
  ""
  "/* For push/pop, emit a REG_INC note to make sure the reload
      inheritance and reload CSE pass notice the change of the stack
      pointer.  */
  if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
    {
      rtx insn;

      insn = emit_insn (gen_movdf_internal (operands[0], operands[1]));
      REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
					  stack_pointer_rtx,
					  REG_NOTES (insn));
      DONE;
    }
")

(define_insn "movdf_internal"
  [(set (match_operand:DF 0 "nonimmediate_operand" "=ou,U,!u,U,m,m,!u")
	(match_operand:DF 1 "general_operand" "G,iU,iU,!u,mi,!u,!mu"))
   (clobber (match_scratch:HI 2 "=X,&d,&d,&d,&d,&d,&d"))]
  ""
  "#")

(define_split
  [(set (match_operand:DF 0 "nonimmediate_operand" "")
	(match_operand:DF 1 "general_operand" ""))
   (clobber (match_scratch:HI 2 ""))]
  "reload_completed"
  [(const_int 0)]
  "m68hc11_split_move (operands[0], operands[1], operands[2]);
   DONE;")

;;--------------------------------------------------------------------
;;- 32-bit Move Operations.
;; The movsi and movsf patterns are identical except for the mode.
;; When we move to/from a hard register (d+x), we don't need a scratch.
;; Otherwise, a scratch register is used as intermediate register for
;; the move.  The '&' constraint is necessary to make sure the reload
;; pass does not give us a register that dies in the insn and is used
;; for input/output operands.
;;--------------------------------------------------------------------
(define_expand "movsi"
  [(parallel [(set (match_operand:SI 0 "nonimmediate_operand" "")
		   (match_operand:SI 1 "general_operand" ""))
	      (clobber (match_scratch:HI 2 ""))])]
  ""
  "/* For push/pop, emit a REG_INC note to make sure the reload
      inheritance and reload CSE pass notice the change of the stack
      pointer.  */
  if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
    {
      rtx insn;

      insn = emit_insn (gen_movsi_internal (operands[0], operands[1]));
      REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
					  stack_pointer_rtx,
					  REG_NOTES (insn));
      DONE;
    }
")

(define_insn "movsi_internal"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=ou,mu,?D,m,?D,?u,?u,!u,D")
	(match_operand:SI 1 "general_operand"      "K,imu,im,?D,!u,?D,mi,!u,!D"))
   (clobber (match_scratch:HI 2                    "=X,&d,X,X,X,X,&d,&d,X"))]
  ""
  "#")

(define_split
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(match_operand:SI 1 "general_operand" ""))
   (clobber (match_scratch:HI 2 ""))]
  "reload_completed"
  [(const_int 0)]
  "m68hc11_split_move (operands[0], operands[1], operands[2]);
   DONE;")

(define_expand "movsf"
  [(parallel [(set (match_operand:SF 0 "nonimmediate_operand" "")
		   (match_operand:SF 1 "general_operand" ""))
	      (clobber (match_scratch:HI 2 ""))])]
  ""
  "/* For push/pop, emit a REG_INC note to make sure the reload
      inheritance and reload CSE pass notice the change of the stack
      pointer.  */
  if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
    {
      rtx insn;

      insn = emit_insn (gen_movsf_internal (operands[0], operands[1]));
      REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
					  stack_pointer_rtx,
					  REG_NOTES (insn));
      DONE;
    }
")

(define_insn "movsf_internal"
  [(set (match_operand:SF 0 "nonimmediate_operand" "=o!u,m,D,m,D,!u,!u,!u,D")
	(match_operand:SF 1 "general_operand" "G,im,im,D,!u,D,mi,!u,!D"))
   (clobber (match_scratch:HI 2 "=X,&d,X,X,X,X,&d,&d,X"))]
  ""
  "#")

(define_split
  [(set (match_operand:SF 0 "nonimmediate_operand" "")
	(match_operand:SF 1 "general_operand" ""))
   (clobber (match_scratch:HI 2 ""))]
  "reload_completed"
  [(const_int 0)]
  "m68hc11_split_move (operands[0], operands[1], operands[2]);
   DONE;")


;;--------------------------------------------------------------------
;;- 16-bit Move Operations.
;; We don't need a scratch register.
;;--------------------------------------------------------------------

(define_insn "*movhi2_push"
  [(set (match_operand:HI 0 "push_operand" "=<,<,<")
	(match_operand:HI 1 "general_operand" "xy,?d,!z"))]
  "TARGET_M6811 && !TARGET_M6812"
  "*
{
  cc_status = cc_prev_status;
  if (D_REG_P (operands[1]))
    {
      output_asm_insn (\"pshb\", operands);
      return \"psha\";
    }
  else if (X_REG_P (operands[1]))
    {
      return \"pshx\";
    }
  else if (Y_REG_P (operands[1]))
    {
      return \"pshy\";
    }
  fatal_insn (\"Invalid register in the instruction\", insn);
}")

(define_insn "*movhi2_pop"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=xy,d")
	(match_operand:HI 1 "pop_operand" ">,>"))]
  "TARGET_M6811"
  "*
{
  cc_status = cc_prev_status;
  if (D_REG_P (operands[0]))
    {
      output_asm_insn (\"pula\", operands);
      return \"pulb\";
    }
  else if (X_REG_P (operands[0]))
    {
      return \"pulx\";
    }
  else if (Y_REG_P (operands[0]))
    {
      return \"puly\";
    }
  fatal_insn (\"Invalid register in the instruction\", insn);
}")

(define_expand "movhi"
  [(set (match_operand:HI 0 "nonimmediate_operand" "")
	(match_operand:HI 1 "general_operand" ""))]
  ""
  "
{
  if (reload_in_progress)
    {
      if (m68hc11_reload_operands (operands))
        {
          DONE;
        }
    }
  if (TARGET_M6811 && (reload_in_progress | reload_completed) == 0)
    {
      if (GET_CODE (operands[0]) == MEM &&
	  (GET_CODE (operands[1]) == MEM
	   || GET_CODE (operands[1]) == CONST_INT))
        {
	  operands[1] = force_reg (HImode, operands[1]);
        }
      else if (IS_STACK_PUSH (operands[0])
	       && GET_CODE (operands[1]) != REG)
        {
	  operands[1] = force_reg (HImode, operands[1]);
        }
    }
  /* For push/pop, emit a REG_INC note to make sure the reload
     inheritance and reload CSE pass notice the change of the stack
     pointer.  */
  if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
    {
      rtx insn;

      insn = emit_insn (gen_rtx (SET, VOIDmode, operands[0], operands[1]));
      REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
					  stack_pointer_rtx,
					  REG_NOTES (insn));
      DONE;
    }
}")

(define_insn "movhi_const0"
  [(set (match_operand:HI 0 "non_push_operand" "=d,A,um")
	(const_int 0))]
  ""
  "@
   clra\\n\\tclrb
   ld%0\\t#0
   clr\\t%b0\\n\\tclr\\t%h0")

(define_insn "*movhi_68hc12"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=U,dAw,U,U,m,!u")
	(match_operand:HI 1 "general_operand" "U,rim,dAwi,!u,dAw,riU"))]
  "TARGET_M6812"
  "*
{
  m68hc11_gen_movhi (insn, operands);
  return \"\";
}")

(define_insn "*movhi_m68hc11"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=dAw,!u,m,m,dAw,!*u")
	(match_operand:HI 1 "general_operand" "dAwim,dAw,dA,?Aw,!*u,dAw"))]
  "TARGET_M6811"
  "*
{
  m68hc11_gen_movhi (insn, operands);
  return \"\";
}")

;;--------------------------------------------------------------------
;;- 8-bit Move Operations.
;; We don't need a scratch register.
;;--------------------------------------------------------------------
;;
;; The *a alternative also clears the high part of the register.
;; This should be ok since this is not the (strict_low_part) set.
;;
(define_insn "movqi_const0"
  [(set (match_operand:QI 0 "non_push_operand" "=d,m,!u,*A,!*q")
	(const_int 0))]
  ""
  "@
   clrb
   clr\\t%b0
   clr\\t%b0
   ld%0\\t#0
   clr%0")

;;
;; 8-bit operations on address registers.
;;
;; Switch temporary to the D register and load the value in B.
;; This is possible as long as the address register does not
;; appear in the source operand.
;;
(define_split
  [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
        (match_operand:QI 1 "general_operand" ""))]
  "z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode
   && !reg_mentioned_p (operands[0], operands[1])
   && !D_REG_P (operands[1])"
  [(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
              (set (match_dup 2) (reg:HI D_REGNUM))])
   (set (reg:QI D_REGNUM) (match_dup 1))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 2))
              (set (match_dup 2) (reg:HI D_REGNUM))])]
  "operands[2] = gen_rtx (REG, HImode, REGNO (operands[0]));")

;;
;; 8-bit operations on address registers.
;;
(define_split
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
        (match_operand:QI 1 "hard_addr_reg_operand" ""))]
  "z_replacement_completed == 2 && GET_MODE (operands[1]) == QImode
   && !reg_mentioned_p (operands[1], operands[0])
   && !D_REG_P (operands[0])"
  [(parallel [(set (reg:HI D_REGNUM) (match_dup 2))
              (set (match_dup 2) (reg:HI D_REGNUM))])
   (set (match_dup 0) (reg:QI D_REGNUM))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 2))
              (set (match_dup 2) (reg:HI D_REGNUM))])]
  "operands[2] = gen_rtx (REG, HImode, REGNO (operands[1]));")

(define_insn "*movqi2_push"
  [(set (match_operand:QI 0 "push_operand" "=<,<")
	(match_operand:QI 1 "general_operand" "d,!*A"))]
  ""
  "*
{
  if (A_REG_P (operands[1]))
    return \"#\";

  cc_status = cc_prev_status;
  return \"pshb\";
}")


(define_expand "movqi"
  [(set (match_operand:QI 0 "nonimmediate_operand" "")
	(match_operand:QI 1 "general_operand" ""))]
  ""
  "
{
  if (reload_in_progress)
    {
      if (m68hc11_reload_operands (operands))
        {
          DONE;
        }
    }
  if (TARGET_M6811 && (reload_in_progress | reload_completed) == 0)
    {
      if (GET_CODE (operands[0]) == MEM
	  && (GET_CODE (operands[1]) == MEM
	      || GET_CODE (operands[1]) == CONST_INT))
        {
	  operands[1] = force_reg (QImode, operands[1]);
        }
      else if (IS_STACK_PUSH (operands[0])
	       && GET_CODE (operands[1]) != REG)
        {
	  operands[1] = force_reg (QImode, operands[1]);
        }
    }
  /* For push/pop, emit a REG_INC note to make sure the reload
     inheritance and reload CSE pass notice the change of the stack
     pointer.  */
  if (IS_STACK_PUSH (operands[0]) || IS_STACK_POP (operands[1]))
    {
      rtx insn;

      insn = emit_insn (gen_rtx (SET, VOIDmode, operands[0], operands[1]));
      REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC,
					  stack_pointer_rtx,
					  REG_NOTES (insn));
      DONE;
    }
}")

(define_insn "*movqi_68hc12"
  [(set (match_operand:QI 0 "nonimmediate_operand" 
				"=U,d*AU*q,d*A*qU,d*A*q,m,?*u,m")
	(match_operand:QI 1 "general_operand" 
				"U,*ri*q,U,m,d*q,*ri*qU,!*A"))]
  "TARGET_M6812"
  "*
{
  m68hc11_gen_movqi (insn, operands);
  return \"\";
}")

(define_insn "*movqi_m68hc11"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d*A*q,m,m,d*A*q,*u")
	(match_operand:QI 1 "general_operand" "d*Aim*q,d*q,!*A,*u,d*A*q"))]
  "TARGET_M6811"
  "*
{
  m68hc11_gen_movqi (insn, operands);
  return \"\";
}")

;;--------------------------------------------------------------------
;;-  Swap registers
;;--------------------------------------------------------------------
;; Swapping registers is used for split patterns.
(define_insn "swap_areg"
   [(set (match_operand:HI 0 "hard_reg_operand" "=d,A")
         (match_operand:HI 1 "hard_reg_operand" "=A,d"))
    (set (match_dup 1) (match_dup 0))]
   ""
   "*
{
  m68hc11_output_swap (insn, operands);
  return \"\";
}")

;;--------------------------------------------------------------------
;;-  Truncation insns.
;;--------------------------------------------------------------------
;;
;; Truncation are not necessary because GCC knows how to truncate,
;; specially when values lie in consecutive registers.
;;

(define_expand "floatunssisf2"
  [(set (match_operand:SF 0 "nonimmediate_operand" "")
	(unsigned_float:SF (match_operand:SI 1 "general_operand" "")))]
  ""
  "m68hc11_emit_libcall (\"__floatunsisf\", UNSIGNED_FLOAT, 
			 SFmode, SImode, 2, operands);
   DONE;")

(define_expand "floatunssidf2"
  [(set (match_operand:DF 0 "nonimmediate_operand" "")
	(unsigned_float:DF (match_operand:SI 1 "general_operand" "")))]
  ""
  "m68hc11_emit_libcall (\"__floatunsidf\", UNSIGNED_FLOAT, 
			 DFmode, SImode, 2, operands);
   DONE;")

;;--------------------------------------------------------------------
;;-  Zero extension insns.
;;--------------------------------------------------------------------

;;
;; 64-bit extend.  The insn will be split into 16-bit instructions just
;; before the final pass.  We need a scratch register for the split.
;; The final value can be generated on the stack directly.  This is more
;; efficient and useful for conversions made during parameter passing rules.
;;
(define_insn "zero_extendqidi2"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=m,!u,m,!u")
	(zero_extend:DI 
	   (match_operand:QI 1 "nonimmediate_operand" "m,dmu,*B,*B")))
   (clobber (match_scratch:HI 2 "=&d,&dB,&d,&dB"))]
  ""
  "#")

(define_split
  [(set (match_operand:DI 0 "push_operand" "")
	(zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))
   (clobber (match_scratch:HI 2 "=&dB"))]
  "z_replacement_completed == 2"
  [(const_int 0)]
  "
{
  rtx low  = m68hc11_gen_lowpart (SImode, operands[0]);
  rtx push = m68hc11_gen_lowpart (HImode, low);
  rtx src  = operands[1];

   /* Source operand must be in a hard register.  */
   if (!H_REG_P (src))
     {
       src = gen_rtx (REG, QImode, REGNO (operands[2]));
       emit_move_insn (src, operands[1]);
     }

   /* Source is in D, we can push B then one word of 0 and we do
      a correction on the stack pointer.  */
   if (D_REG_P (src))
     {
       emit_move_insn (m68hc11_gen_lowpart (QImode, push), src);
       emit_move_insn (operands[2], const0_rtx);
       if (D_REG_P (operands[2]))
	 {
	   emit_move_insn (m68hc11_gen_lowpart (QImode, push), src);
	 }
       else
	 {
           emit_move_insn (push, operands[2]);
           emit_insn (gen_addhi3 (gen_rtx (REG, HImode, HARD_SP_REGNUM),
				  gen_rtx (REG, HImode, HARD_SP_REGNUM),
			          const1_rtx));
	 }
     }
   else
     {
       /* Source is in X or Y.  It's better to push the 16-bit register
          and then to some stack adjustment.  */
       src = gen_rtx (REG, HImode, REGNO (src));
       emit_move_insn (push, src);
       emit_move_insn (operands[2], const0_rtx);
       emit_insn (gen_addhi3 (gen_rtx (REG, HImode, HARD_SP_REGNUM),
			      gen_rtx (REG, HImode, HARD_SP_REGNUM),
			      const1_rtx));
       emit_move_insn (push, operands[2]);
       emit_insn (gen_addhi3 (gen_rtx (REG, HImode, HARD_SP_REGNUM),
			      gen_rtx (REG, HImode, HARD_SP_REGNUM),
			      const1_rtx));
     }      
   emit_move_insn (push, operands[2]);
   emit_move_insn (push, operands[2]);
   emit_move_insn (push, operands[2]);
   DONE;
}")

(define_split
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))
   (clobber (match_scratch:HI 2 "=&dB"))]
  "z_replacement_completed == 2"
  [(const_int 0)]
  "
{
  rtx low  = m68hc11_gen_lowpart (SImode, operands[0]);
  rtx low2 = m68hc11_gen_lowpart (HImode, low);
  rtx src  = operands[1];

   /* Source operand must be in a hard register.  */
   if (!H_REG_P (src))
     {
       src = gen_rtx (REG, QImode, REGNO (operands[2]));
       emit_move_insn (src, operands[1]);
     }

   emit_move_insn (m68hc11_gen_lowpart (QImode, low2), src);
   emit_move_insn (operands[2], const0_rtx);
   src = gen_rtx (REG, QImode, REGNO (operands[2]));
   emit_move_insn (m68hc11_gen_highpart (QImode, low2), src);

   emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
   low = m68hc11_gen_highpart (SImode, operands[0]);
   emit_move_insn (m68hc11_gen_lowpart (HImode, low), operands[2]);
   emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
   DONE;
}")

(define_insn "zero_extendhidi2"
  [(set (match_operand:DI 0 "non_push_operand" "=m,m,m,m,!u,!u")
	(zero_extend:DI 
	    (match_operand:HI 1 "nonimmediate_operand" "m,d,A,!u,dmA,!u")))
   (clobber (match_scratch:HI 2 "=&d,&B,&d,&dB,&dB,&dB"))]
  ""
  "#")

(define_split
  [(set (match_operand:DI 0 "non_push_operand" "")
	(zero_extend:DI 
	    (match_operand:HI 1 "nonimmediate_operand" "")))
   (clobber (match_scratch:HI 2 ""))]
  "z_replacement_completed == 2"
  [(const_int 0)]
  "
{
   rtx low  = m68hc11_gen_lowpart (SImode, operands[0]);
   rtx high = m68hc11_gen_highpart (SImode, operands[0]);
   rtx src  = operands[1];

   /* Make sure the source is in a hard register.  */
   if (!H_REG_P (src))
     {
       src = operands[2];
       emit_move_insn (src, operands[1]);
     }

   /* Move the low part first for the push.  */
   emit_move_insn (m68hc11_gen_lowpart (HImode, low), src);

   /* Now, use the scratch register to fill in the zeros.  */
   emit_move_insn (operands[2], const0_rtx);
   emit_move_insn (m68hc11_gen_highpart (HImode, low), operands[2]);
   emit_move_insn (m68hc11_gen_lowpart (HImode, high), operands[2]);
   emit_move_insn (m68hc11_gen_highpart (HImode, high), operands[2]);
   DONE;
}")

(define_insn "zero_extendsidi2"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=m,m,!u,!u")
	(zero_extend:DI 
	    (match_operand:SI 1 "nonimmediate_operand" "m,Du,m,Du")))
   (clobber (match_scratch:HI 2 "=d,d,d,d"))]
  ""
  "#")

(define_split 
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(zero_extend:DI 
	    (match_operand:SI 1 "nonimmediate_operand" "")))
   (clobber (match_scratch:HI 2 ""))]
  "z_replacement_completed == 2"
  [(const_int 0)]
  "
{
  rtx low  = m68hc11_gen_lowpart (SImode, operands[0]);
  rtx high = m68hc11_gen_highpart (SImode, operands[0]);

  /* Move the low part first so that this is ok for a push.  */
  m68hc11_split_move (low, operands[1], operands[2]);

  /* Use the scratch register to clear the high part of the destination.  */
  emit_move_insn (operands[2], const0_rtx);
  emit_move_insn (m68hc11_gen_lowpart (HImode, high), operands[2]);
  emit_move_insn (m68hc11_gen_highpart (HImode, high), operands[2]);
  DONE;
}")

;;
;; For 16->32bit unsigned extension, we don't allow generation on the stack
;; because it's less efficient.
;;
(define_insn "zero_extendhisi2"
  [(set (match_operand:SI 0 "non_push_operand" "=D,m,u,m,m,!u,!u")
        (zero_extend:SI 
	    (match_operand:HI 1 "nonimmediate_operand" "dAmu,dA,dA,m,!u,m,!u")))
   (clobber (match_scratch:HI 2 "=X,X,X,&d,&dB,&dB,&dB"))]
  ""
  "#")

(define_split
  [(set (match_operand:SI 0 "non_push_operand" "")
	(zero_extend:SI 
	    (match_operand:HI 1 "nonimmediate_operand" "")))
   (clobber (match_scratch:HI 2 ""))]
  "reload_completed"
  [(const_int 0)]
  "
{
  rtx src = operands[1];

  if (!H_REG_P (src) && !H_REG_P (operands[0]))
    {
      src = operands[2];
      emit_move_insn (src, operands[1]);
    }
  emit_move_insn (m68hc11_gen_lowpart (HImode, operands[0]), src);
  emit_move_insn (m68hc11_gen_highpart (HImode, operands[0]), const0_rtx);
  DONE;
}")

(define_insn "zero_extendqisi2"
  [(set (match_operand:SI 0 "non_push_operand" "=D,D,m,m,u")
      (zero_extend:SI 
	  (match_operand:QI 1 "nonimmediate_operand" "dmu,xy,d,xy,dxy")))]
  ""
  "#")

(define_split 
  [(set (match_operand:SI 0 "non_push_operand" "")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
  "reload_completed && !X_REG_P (operands[0])"
  [(set (match_dup 2) (zero_extend:HI (match_dup 1)))
   (set (match_dup 3) (const_int 0))]
  "
   operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
   operands[3] = m68hc11_gen_highpart (HImode, operands[0]);")

(define_split 
  [(set (match_operand:SI 0 "hard_reg_operand" "")
	(zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
  "z_replacement_completed == 2 && X_REG_P (operands[0])"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (const_int 0))
   (set (match_dup 5) (zero_extend:HI (match_dup 6)))]
  "
   if (X_REG_P (operands[1]))
     {
	emit_insn (gen_swap_areg (gen_rtx (REG, HImode, HARD_D_REGNUM),
				  gen_rtx (REG, HImode, HARD_X_REGNUM)));
	emit_insn (gen_zero_extendqihi2 (gen_rtx (REG, HImode, HARD_D_REGNUM),
					 gen_rtx (REG, QImode, HARD_D_REGNUM)));
	emit_move_insn (gen_rtx (REG, HImode, HARD_X_REGNUM),
			const0_rtx);
	DONE;
     }

   if (reg_mentioned_p (gen_rtx (REG, HImode, HARD_X_REGNUM), operands[1]))
     {
	emit_insn (gen_zero_extendqihi2 (m68hc11_gen_lowpart (HImode,
							      operands[0]),
					 operands[1]));
	emit_move_insn (gen_rtx (REG, HImode, HARD_X_REGNUM), const0_rtx);
	DONE;
     }
   operands[4] = m68hc11_gen_highpart (HImode, operands[0]);
   operands[5] = m68hc11_gen_lowpart (HImode, operands[0]);
   if (A_REG_P (operands[1]))
     {
       operands[2] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
       operands[3] = gen_rtx (REG, HImode, REGNO (operands[1]));
       operands[6] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);
     }
   else
     {
       operands[5] = operands[2] =
       operands[3] = gen_rtx (REG, HImode, HARD_D_REGNUM);
       operands[6] = operands[1];
     }
")

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "non_push_operand" "=dm,d,*A,!*u,d,m,!*u")
	(zero_extend:HI 
	    (match_operand:QI 1 "nonimmediate_operand" "d,*A,d*Am,d,!um,*A,*A")))]
  ""
 "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  if (H_REG_P (operands[0]))
    {
      output_asm_insn (\"clra\", operands);
      if (operands[0] != operands[1]
          && !(D_REG_P (operands[0]) && D_REG_P (operands[1])))
        {
          if (X_REG_P (operands[1])
	      || (D_REG_P (operands[1]) && X_REG_P (operands[0])))
	    {
	      output_asm_insn (\"stx\\t%t1\", operands);
	      output_asm_insn (\"ldab\\t%T0\", operands);
	    }
	  else if (Y_REG_P (operands[1])
		   || (D_REG_P (operands[1]) && Y_REG_P (operands[0])))
	    {
	      output_asm_insn (\"sty\\t%t1\", operands);
	      output_asm_insn (\"ldab\\t%T0\", operands);
	    }
          else
            {
	      output_asm_insn (\"ldab\\t%b1\", operands);
            }
	  cc_status.flags |= CC_NOT_NEGATIVE;
        }
      else
	{
	  /* Status refers to the clra insn. Status is ok for others
	   * since we have loaded the value in B.
	   */
	  CC_STATUS_INIT;
	}
      return \"\";
    }

  if (A_REG_P (operands[1]))
    {
      output_asm_insn (\"st%1\\t%0\", operands);
      output_asm_insn (\"clr\\t%h0\", operands);
      CC_STATUS_INIT;
    }
  else
    {
      output_asm_insn (\"clr\\t%h0\", operands);
      output_asm_insn (\"stab\\t%b0\", operands);
      cc_status.flags |= CC_NOT_NEGATIVE;
    }

  return \"\";
}")


;;--------------------------------------------------------------------
;;-  Sign extension insns.
;;--------------------------------------------------------------------

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=D,m,u")
	(sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "dmux,d,d")))]
  ""
  "*
{
  rtx ops[3];
  int need_tst = 0;

  /* The 68HC12 has a sign-extension instruction.  Use it when the
     destination is the register (X,D).  First sign-extend the low
     part and fill X with the sign-extension of the high part.  */
  if (TARGET_M6812 && X_REG_P (operands[0]))
    {
      if (!D_REG_P (operands[1]))
        {
	  ops[0] = gen_rtx (REG, QImode, HARD_D_REGNUM);
	  ops[1] = operands[1];
	  m68hc11_gen_movqi (insn, ops);
	}
      return \"sex\\tb,d\\n\\tsex\\ta,x\";
    }

  ops[2] = gen_label_rtx ();

  if (X_REG_P (operands[1]))
    {
      output_asm_insn (\"xgdx\", operands);
      need_tst = 1;
    }
  else if (X_REG_P (operands[0]))
    {
      /* X can be used as an indexed addressing in the source.
         Get the value before clearing it.  */
      if (reg_mentioned_p (ix_reg, operands[1]))
        {
          output_asm_insn (\"ldab\\t%b1\", operands);
	  need_tst = 1;
        }
      output_asm_insn (\"ldx\\t#0\", operands);
    }

  output_asm_insn (\"clra\", operands);
  if (!X_REG_P (operands[0]))
    {
      ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
      ops[1] = m68hc11_gen_lowpart (QImode, ops[0]);

      if (IS_STACK_PUSH (operands[0]))
        {
          output_asm_insn (\"pshb\", ops);
          output_asm_insn (\"tstb\", ops);
        }
      else
        {
          output_asm_insn (\"stab\\t%b1\", ops);
        }
    }
  else if (D_REG_P (operands[1]) || need_tst)
    {
      output_asm_insn (\"tstb\", operands);
    }
  else
    {
      output_asm_insn (\"ldab\\t%b1\", operands);
    }
  output_asm_insn (\"bpl\\t%l2\", ops);
  output_asm_insn (\"deca\", operands);
  if (X_REG_P (operands[0]))
    output_asm_insn (\"dex\", operands);

  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));

  if (!X_REG_P (operands[0]))
    {
      if (IS_STACK_PUSH (operands[0]))
	{
	  output_asm_insn (\"psha\", ops);
	  output_asm_insn (\"psha\", ops);
	  output_asm_insn (\"psha\", ops);
	}
      else
	{
	  output_asm_insn (\"staa\\t%h0\", ops);

	  ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
	  if (dead_register_here (insn, d_reg))
	    {
	      output_asm_insn (\"tab\", ops);
	      output_asm_insn (\"std\\t%0\", ops);
	    }
	  else
	    {
	      output_asm_insn (\"staa\\t%b0\", ops);
	      output_asm_insn (\"staa\\t%h0\", ops);
	    }
	}
    }

  CC_STATUS_INIT;
  return \"\";
}")


(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "non_push_operand" "=d,*x*ym,u")
	(sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "dum,0,0")))]
  ""
  "*
{
  rtx ops[2];

  if (A_REG_P (operands[0]))
    return \"#\";

  ops[0] = gen_label_rtx ();
  if (D_REG_P (operands[0]))
    {
      if (TARGET_M6812)
	{
	  if (!D_REG_P (operands[1]))
	    {
	      ops[0] = gen_rtx (REG, QImode, HARD_D_REGNUM);
	      ops[1] = operands[1];
	      m68hc11_gen_movqi (insn, ops);
	    }
	  return \"sex\\tb,d\";
	}
      output_asm_insn (\"clra\", operands);
      if (H_REG_P (operands[1]))
        {
          output_asm_insn (\"tstb\", operands);
        }
      else
        {
	  output_asm_insn (\"ldab\\t%b1\", operands);
        }
      output_asm_insn (\"bpl\\t%l0\", ops);
      output_asm_insn (\"deca\", operands);

      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", 
				 CODE_LABEL_NUMBER (ops[0]));
    }
   else
    {
      output_asm_insn (\"clr\\t%h0\", operands);
      if (m68hc11_register_indirect_p (operands[1], HImode))
        {
	  ops[1] = operands[1];
          output_asm_insn (\"brclr\\t%b1 #0x80 %l0\", ops);
	  CC_STATUS_INIT;
        }
      else
        {
          output_asm_insn (\"tst\\t%b1\", operands);
	  output_asm_insn (\"bpl\\t%l0\", ops);
        }
      output_asm_insn (\"dec\\t%h0\", operands);
      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
			         CODE_LABEL_NUMBER (ops[0]));
    }

  return \"\";
}")

;;
;; Split the special case where the source of the sign extend is
;; either Y or Z. In that case, we can't move the source in the D
;; register directly. The movhi pattern handles this move by using
;; a temporary scratch memory location.
;;
(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(sign_extend:SI (match_operand:HI 1 "register_operand" "")))]
  "reload_completed && (Y_REG_P (operands[1]) || Z_REG_P (operands[1]))"
  [(set (reg:HI D_REGNUM) (match_dup 1))
   (set (match_dup 0) (sign_extend:SI (reg:HI D_REGNUM)))]
  "")

(define_insn "extendhisi2"
  [(set (match_operand:SI 0 "register_operand" "=D,D,D")
	(sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "m,!r,dA")))]
  ""
  "*
{
  rtx ops[2];
  int x_reg_used;

  if (Y_REG_P (operands[1]))
    return \"#\";

  if (X_REG_P (operands[1]))
    {
      output_asm_insn (\"xgdx\", operands);
      x_reg_used = 1;
    }
  else
    {
      /* X can be used as an indexed addressing in the source.
         Get the value before clearing it.  */
      x_reg_used = reg_mentioned_p (ix_reg, operands[1]);
      if (x_reg_used)
        {
	  ops[0] = gen_rtx (REG, HImode, HARD_D_REGNUM);
	  ops[1] = operands[1];
	  m68hc11_gen_movhi (insn, ops);
        }
    }

  CC_STATUS_INIT;
  if (TARGET_M6812 && 0)
    {
      /* This sequence of code is larger than the one for 68HC11.
         Don't use it; keep it for documentation.  */
      if (!D_REG_P (operands[1]) && !x_reg_used)
        {
          ops[0] = gen_rtx (REG, HImode, HARD_D_REGNUM);
          ops[1] = operands[1];
          m68hc11_gen_movhi (insn, ops);
        }
      output_asm_insn (\"sex\\ta,x\", operands);
      output_asm_insn (\"xgdx\", operands);
      output_asm_insn (\"sex\\ta,d\", operands);
      return \"xgdx\";
    }

  output_asm_insn (\"ldx\\t#0\", operands);
  if (D_REG_P (operands[1]) || x_reg_used)
    {
      output_asm_insn (\"tsta\", operands);
    }
  else
    {
      ops[0] = gen_rtx (REG, HImode, HARD_D_REGNUM);
      ops[1] = operands[1];
      m68hc11_gen_movhi (insn, ops);
    }

  ops[0] = gen_label_rtx ();
  output_asm_insn (\"bpl\\t%l0\", ops);
  output_asm_insn (\"dex\", operands);
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));

  return \"\";
}")


;;--------------------------------------------------------------------
;;- Min and Max instructions (68HC12).
;;--------------------------------------------------------------------
(define_insn "uminqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m")
	(umin:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
		 (match_operand:QI 2 "general_operand" "m,d")))]
  "TARGET_M6812 && TARGET_MIN_MAX"
  "*
{
  /* Flags are set according to (sub:QI (operand 1) (operand2)).
     The mina/minm use A as the source or destination.  This is the
     high part of D.  There is no way to express that in the pattern
     so we must use 'exg a,b' to put the operand in the good register.  */
  CC_STATUS_INIT;
  if (D_REG_P (operands[0]))
    {
      return \"exg\\ta,b\\n\\tmina\\t%2\\n\\texg\\ta,b\";
    }
  else
    {
      return \"exg\\ta,b\\n\\tminm\\t%0\\n\\texg\\ta,b\";
    }
}")

(define_insn "umaxqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m")
	(umax:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
		 (match_operand:QI 2 "general_operand" "m,d")))]
  "TARGET_M6812 && TARGET_MIN_MAX"
  "*
{
  /* Flags are set according to (sub:QI (operand 1) (operand2)).
     The maxa/maxm use A as the source or destination.  This is the
     high part of D.  There is no way to express that in the pattern
     so we must use 'exg a,b' to put the operand in the good register.  */
  CC_STATUS_INIT;
  if (D_REG_P (operands[0]))
    {
      return \"exg\\ta,b\\n\\tmaxa\\t%2\\n\\texg\\ta,b\";
    }
  else
    {
      return \"exg\\ta,b\\n\\tmaxm\\t%0\\n\\texg\\ta,b\";
    }
}")

(define_insn "uminhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=d,m")
	(umin:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
		 (match_operand:HI 2 "general_operand" "m,d")))]
  "TARGET_M6812 && TARGET_MIN_MAX"
  "*
{
  /* Flags are set according to (sub:HI (operand 1) (operand2)).  */
  CC_STATUS_INIT;
  if (D_REG_P (operands[0]))
    {
      return \"emind\\t%2\";
    }
  else
    {
      return \"eminm\\t%0\";
    }
}")

(define_insn "umaxhi3"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=d,m")
	(umax:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0")
		 (match_operand:HI 2 "general_operand" "m,d")))]
  "TARGET_M6812 && TARGET_MIN_MAX"
  "*
{
  /* Flags are set according to (sub:HI (operand 1) (operand2)).  */
  CC_STATUS_INIT;
  if (D_REG_P (operands[0]))
    {
      return \"emaxd\\t%2\";
    }
  else
    {
      return \"emaxm\\t%0\";
    }
}")


;;--------------------------------------------------------------------
;;- Add instructions.
;;--------------------------------------------------------------------
;; 64-bit: Use a library call because what GCC generates is huge.
;;
(define_expand "adddi3"
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(plus:DI (match_operand:DI 1 "general_operand" "")
		 (match_operand:DI 2 "general_operand" "")))]
  ""
  "m68hc11_emit_libcall (\"___adddi3\", PLUS, DImode, DImode, 3, operands);
   DONE;")

;;
;; - 32-bit Add.
;;
(define_expand "addsi3"
  [(parallel [(set (match_operand:SI 0 "register_operand" "")
	             (plus:SI (match_operand:SI 1 "general_operand" "")
		              (match_operand:SI 2 "general_operand" "")))
              (clobber (match_scratch:HI 3 ""))])]
  ""
  "")

(define_insn "*addsi3_zero_extendhi"
  [(set (match_operand:SI 0 "register_operand" "=D,D,D,D")
	(plus:SI (zero_extend:SI 
		 (match_operand:HI 1 "general_operand" "dxi,!u,mdxi,!u"))
		 (match_operand:SI 2 "general_operand" "mi,mi,D?u,!Du")))
   (clobber (match_scratch:HI 3 "=X,X,X,X"))]
  ""
  "*
{
  rtx ops[3];

  if (X_REG_P (operands[2]))
    {
      ops[0] = operands[1];
    }
  else
    {
      if (X_REG_P (operands[1]))
        {
          output_asm_insn (\"xgdx\", ops);
        }
      else if (!D_REG_P (operands[1]))
        {
          ops[0] = gen_rtx (REG, HImode, HARD_D_REGNUM);
          ops[1] = operands[1];
          m68hc11_gen_movhi (insn, ops);
        }
      ops[0] = m68hc11_gen_lowpart (HImode, operands[2]);
      ops[1] = m68hc11_gen_highpart (HImode, operands[2]);
    }
  ops[2] = gen_label_rtx ();

  /* ldx preserves the carry, propagate it by incrementing X directly.  */
  output_asm_insn (\"addd\\t%0\", ops);
  if (!X_REG_P (operands[2]))
    output_asm_insn (\"ldx\\t%1\", ops);

  output_asm_insn (\"bcc\\t%l2\", ops);
  output_asm_insn (\"inx\", ops);

  CC_STATUS_INIT;
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));
  return \"\";  
}")


(define_split /* "*addsi3_zero_extendqi" */
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (zero_extend:SI 
		   (match_operand:QI 1 "general_operand" ""))
		 (match_operand:SI 2 "memory_operand" "")))
   (clobber (match_scratch:HI 3 "=X,X"))]
  "reload_completed"
  [(set (reg:HI D_REGNUM) (zero_extend:HI (match_dup 1)))
   (parallel [(set (match_dup 0) 
		   (plus:SI (zero_extend:SI (reg:HI D_REGNUM)) (match_dup 2)))
	      (clobber (match_dup 3))])]
  "")

(define_insn "*addsi3_zero_extendqi"
  [(set (match_operand:SI 0 "register_operand" "=D,D")
	(plus:SI (zero_extend:SI 
		   (match_operand:QI 1 "general_operand" "dAmi,!dAmiu"))
		 (match_operand:SI 2 "general_operand" "miD,!muiD")))
   (clobber (match_scratch:HI 3 "=X,X"))]
  ""
  "*
{
  rtx ops[4];

  if (GET_CODE (operands[2]) == MEM)
    return \"#\";

  if (X_REG_P (operands[2]))
    {
      if (H_REG_P (operands[1]))
	{
	  ops[0] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
	  ops[1] = gen_rtx (REG, HImode, REGNO (operands[1]));
	  m68hc11_gen_movhi (insn, ops);
	}
      else
	{
	  ops[0] = operands[1];
	}
      ops[1] = const0_rtx;
    }
  else
    {
      if (X_REG_P (operands[1]))
        {
          output_asm_insn (\"xgdx\", ops);
        }
      else if (!D_REG_P (operands[1]))
        {
          ops[0] = gen_rtx (REG, QImode, HARD_D_REGNUM);
          ops[1] = operands[1];
          m68hc11_gen_movqi (insn, ops);
        }

      ops[0] = m68hc11_gen_lowpart (HImode, operands[2]);
      ops[1] = ops[0];
      ops[2] = m68hc11_gen_highpart (HImode, operands[2]);
      output_asm_insn (\"clra\", ops);
    }

  /* ldx preserves the carry, propagate it by incrementing X directly.  */
  output_asm_insn (\"addb\\t%b0\", ops);
  output_asm_insn (\"adca\\t%h1\", ops);
  if (!X_REG_P (operands[2]))
    output_asm_insn (\"ldx\\t%2\", ops);

  /* If the above adca was adding some constant, we don't need to propagate
     the carry unless the constant was 0xff.  */
  if (X_REG_P (operands[2])
      || GET_CODE (ops[1]) != CONST_INT
      || ((INTVAL (ops[1]) & 0x0ff00) == 0x0ff00))
    {
      ops[3] = gen_label_rtx ();

      output_asm_insn (\"bcc\\t%l3\", ops);
      output_asm_insn (\"inx\", ops);

      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[3]));
    }
  CC_STATUS_INIT;
  return \"\";  
}")

(define_insn "*addsi3"
  [(set (match_operand:SI 0 "non_push_operand" "=o,D,!u,?D,D")
	(plus:SI (match_operand:SI 1 "non_push_operand" "%0,0,0,0,0")
		 (match_operand:SI 2 "general_operand" "ML,i,ML,?D,?oiu")))
   (clobber (match_scratch:HI 3 "=d,X,d,X,X"))]
  ""
  "*
{
  rtx   ops[3];
  const char* add_insn;
  const char* inc_insn;
  const char* incb_mem;
  const char* inch_mem;
  HOST_WIDE_INT val;

  if (which_alternative > 2)
    {
      return \"#\";
    }

  val = INTVAL (operands[2]);
  if ((val & 0x0ffffL) == 0)
    {
      if (!H_REG_P (operands[0]))
	{
	  ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
	  ops[1] = m68hc11_gen_highpart (HImode, operands[2]);
	  output_asm_insn (\"ldd\\t%0\", ops);
	  output_asm_insn (\"addd\\t%1\", ops);
	  output_asm_insn (\"std\\t%0\", ops);
	  return \"\";
	}
      else if (val == 1)
	{
	  return \"inx\";
	}
      else
	{
	  return \"#\";
	}
    }
  if ((val & 0xffff0000L) != 0 && (val & 0xffff0000L) != 0xffff0000L)
    {
      return \"#\";
    }

  if (val >= 0)
    {
      ops[1]   = operands[2];
      add_insn = \"addd\\t%1\";
      inc_insn = \"inx\\t\";
      incb_mem  = \"inc\\t%b1\";
      inch_mem  = \"inc\\t%h1\";
    }
  else
    {
      ops[1] = GEN_INT (- val);
      add_insn = \"subd\\t%1\";
      inc_insn = \"dex\";
      incb_mem  = \"dec\\t%b1\";
      inch_mem  = \"dec\\t%h1\";
    }
      
  ops[2] = gen_label_rtx ();
  if (!H_REG_P (operands[0]))
    {
      ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
      output_asm_insn (\"ldd\\t%0\", ops);
    }
  output_asm_insn (add_insn, ops);
  if (!H_REG_P (operands[0]))
    {
      output_asm_insn (\"std\\t%0\", ops);
    }
  output_asm_insn (\"bcc\\t%l2\", ops);
  if (H_REG_P (operands[0]))
    {
      output_asm_insn (inc_insn, ops);
    }
  else
    {
      ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
      ops[1] = ops[0];
      if (INTVAL (operands[2]) < 0)
	{
	  output_asm_insn (\"ldd\\t%1\", ops);
	  output_asm_insn (\"addd\\t#-1\", ops);
	  output_asm_insn (\"std\\t%1\", ops);
	}
      else
	{
          output_asm_insn (incb_mem, ops);
          output_asm_insn (\"bne\\t%l2\", ops);
          output_asm_insn (inch_mem, ops);
	}
    }
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[2]));

  CC_STATUS_INIT;
  return \"\";
}")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "const_int_operand" "")))
   (clobber (match_scratch:HI 3 ""))]
  "reload_completed && z_replacement_completed == 2
   && ((INTVAL (operands[2]) & 0x0FFFF) == 0)"
  [(set (match_dup 5) (match_dup 6))
   (set (reg:HI 0) (plus:HI (reg:HI 0) (match_dup 4)))
   (set (match_dup 6) (match_dup 5))]
  "operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
   if (X_REG_P (operands[0]))
     {
       operands[5] = operands[6] = gen_rtx (REG, HImode, HARD_D_REGNUM);
     }
   else
     {
       operands[6] = m68hc11_gen_highpart (HImode, operands[1]);
       operands[5] = operands[3];
     }
   ")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(plus:SI (match_operand:SI 1 "register_operand" "")
		 (match_operand:SI 2 "general_operand" "")))
   (clobber (match_scratch:HI 3 "=X"))]
  "reload_completed && z_replacement_completed == 2
   && (GET_CODE (operands[2]) != CONST_INT || 
        (!(INTVAL (operands[2]) >= -65536 && INTVAL (operands[2]) <= 65535)))"
  [(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
   (parallel [(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))
              (set (reg:HI X_REGNUM) (reg:HI D_REGNUM))])
   (set (reg:QI B_REGNUM) (plus:QI (plus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
   (set (reg:QI A_REGNUM) (plus:QI (plus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
   (parallel [(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))
              (set (reg:HI X_REGNUM) (reg:HI D_REGNUM))])]
  "operands[3] = m68hc11_gen_lowpart (HImode, operands[2]);
   operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
   operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
   operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")

;;
;; Instruction generated to propagate the carry of a 16-bit add
;; to the upper 16-bit part (in register X).
;;
(define_insn "*addsi_carry"
  [(set (match_operand:HI 0 "register_operand" "=x")
           (plus:HI (plus:HI (match_operand:HI 1 "register_operand" "0")
		             (const_int 0)) 
		    (reg:HI CC_REGNUM)))]
  ""
  "*
{
  rtx ops[2];

  ops[0] = gen_label_rtx ();
  output_asm_insn (\"bcc\\t%l0\", ops);
  output_asm_insn (\"in%0\", operands);
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
  CC_STATUS_INIT;
  return \"\";
}")

;;
;; - 16-bit Add.
;;
(define_expand "addhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	   (plus:HI (match_operand:HI 1 "register_operand" "")
		    (match_operand:HI 2 "general_operand" "")))]
  ""
  "
{
  if (TARGET_M6811 && SP_REG_P (operands[0]))
    {
      emit_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
			 gen_rtx (SET, VOIDmode,
				  operand0,
				  gen_rtx (PLUS, HImode,
					   operand1, operand2)),
			gen_rtx (CLOBBER, VOIDmode,
				gen_rtx (SCRATCH, HImode)))));
      DONE;
    }
}")

(define_insn "*addhi3_68hc12"
  [(set (match_operand:HI 0 "register_operand" "=xyd,d,xy*z*w,xy*z*w,xy*z")
        (plus:HI (match_operand:HI 1 "register_operand" "%0,0,0,xy*zw,0")
                 (match_operand:HI 2 "general_operand" "i,m*A*wu,id,id,!mu*A")))]
  "TARGET_M6812"
  "*
{
  int val;
  const char* insn_code;

  if (which_alternative >= 4)
    {
      if (A_REG_P (operands[2]))
        {
	  CC_STATUS_INIT;
	  output_asm_insn (\"xgd%2\", operands);
	  output_asm_insn (\"lea%0 d,%0\", operands);
	  return \"xgd%2\";
	}
      return \"#\";
    }

  if (D_REG_P (operands[0]))
    {
      if (X_REG_P (operands[2]))
	{
	  m68hc11_notice_keep_cc (operands[0]);
	  output_asm_insn (\"xgdx\", operands);
	  output_asm_insn (\"leax\\td,%2\", operands);
	  return \"xgdx\";
	}
      else if (Y_REG_P (operands[2]))
	{
	  m68hc11_notice_keep_cc (operands[0]);
	  output_asm_insn (\"xgdy\", operands);
	  output_asm_insn (\"leay\\td,%2\", operands);
	  return \"xgdy\";
	}
      else if (SP_REG_P (operands[2]))
	{
	  output_asm_insn (\"sts\\t%t0\", operands);
	  return \"addd\\t%t0\";
	}
      return \"addd\\t%2\";
    }

  if (GET_CODE (operands[2]) == CONST_INT)
    val = INTVAL (operands[2]);
  else
    val = 1000;

  if ((val != -1 && val != 1) || !rtx_equal_p (operands[0], operands[1]))
    {
      m68hc11_notice_keep_cc (operands[0]);
      switch (REGNO (operands[0]))
	{
	case HARD_X_REGNUM:
	  return \"leax\\t%i2,%1\";

	case HARD_Y_REGNUM:
	  return \"leay\\t%i2,%1\";

	case HARD_SP_REGNUM:
	  return \"leas\\t%i2,%1\";

	default:
	  fatal_insn (\"Invalid operands in the instruction\", insn);
	}
    }
  if (val > 0)
    {
      insn_code = X_REG_P (operands[0]) ? \"inx\"
		: Y_REG_P (operands[0]) ? \"iny\" : \"ins\";
    }
  else
    {
      val  = -val;
      insn_code = X_REG_P (operands[0]) ? \"dex\"
		: Y_REG_P (operands[0]) ? \"dey\" : \"des\";
    }

  /* For X and Y increment, the flags are not complete. Only the Z flag
     is updated. For SP increment, flags are not changed.  */
  if (SP_REG_P (operands[0]))
    {
      cc_status = cc_prev_status; 
      if (INTVAL (operands[2]) < 0)
	{
	  while (val > 2)
	    {
	      output_asm_insn (\"pshx\", operands);
	      val -= 2;
	    }
	  if (val == 0)
	    return \"\";
	}     
    }
  else
    {
      CC_STATUS_INIT;
    }

  while (val)
    {
      output_asm_insn (insn_code, operands);
      val--;
    }
  return \"\";
}")

;;
;; Specific pattern to add to the stack pointer.
;; We also take care of the clobbering of the IY register.
;;
(define_insn "addhi_sp"
  [(set (match_operand:HI 0 "stack_register_operand" "=w,w,w,w")
	  (plus:HI (match_operand:HI 1 "stack_register_operand" "%0,0,0,0")
		   (match_operand:HI 2 "general_operand" "P,im,u,im")))
   (clobber (match_scratch:HI 3 "=X,&y,&y,!&x"))]
  "!TARGET_M6812"
  "*
{
  HOST_WIDE_INT val;

  if (optimize && Y_REG_P (operands[3])
      && dead_register_here (insn, gen_rtx (REG, HImode, HARD_X_REGNUM)))
    operands[3] = gen_rtx (REG, HImode, HARD_X_REGNUM);

  if (GET_CODE (operands[2]) == CONST_INT
      && (val = INTVAL (operands[2])) != 0
      && (CONST_OK_FOR_LETTER_P (val, 'P')
	  || (val > 0 && val <= 8)))
    {
      while (val > 1 || val < -1)
	{
	  if (val > 0)
	    {
	      if (!H_REG_P (operands[3]))
		break;

	      output_asm_insn (\"pul%3\", operands);
	      val -= 2;
	    }
	  else
	    {
	      output_asm_insn (\"pshx\", operands);
	      val += 2;
	    }
	}
      while (val != 0)
	{
	  if (val > 0)
	    {
	      output_asm_insn (\"ins\", operands);
	      val--;
	    }
	  else
	    {
	      output_asm_insn (\"des\", operands);
	      val++;
	    }
	}
      cc_status = cc_prev_status;
      return \"\";
    }

  /* Need to transfer to SP to X/Y and then to D register.
     Register X/Y is lost, this is specified by the (clobber) statement.  */
  output_asm_insn (\"ts%3\", operands);
  if (GET_CODE (operands[2]) == CONST_INT
      && ((val = INTVAL (operands[2])) >= 0 && val < 0x100)
      && dead_register_here (insn, gen_rtx (REG, HImode, HARD_D_REGNUM)))
    {
      output_asm_insn (\"ldab\\t%2\", operands);
      output_asm_insn (\"ab%3\", operands);
      CC_STATUS_INIT;
    }
  else
    {
      output_asm_insn (\"xgd%3\", operands);
      output_asm_insn (\"addd\\t%2\", operands);
      output_asm_insn (\"xgd%3\", operands);
    }

   /* The status flags correspond to the addd.  xgdy and tys do not
      modify the flags.  */
  return \"t%3s\";
}")

(define_insn "*addhi3"
  [(set (match_operand:HI 0 "hard_reg_operand" "=A,dA,d,!A,d*A,!d*A")
	(plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0,0,0")
		 (match_operand:HI 2 "general_operand" "N,I,i,I,mi*A*d,!u*d*w")))]
  "TARGET_M6811"
  "*
{
  const char* insn_code;
  int val;

  if (D_REG_P (operands[0]) && SP_REG_P (operands[2]))
    {
      output_asm_insn (\"sts\\t%t0\", operands);
      output_asm_insn (\"addd\\t%t0\", operands);
      return \"addd\\t#1\";
    }
  if (GET_CODE (operands[2]) != CONST_INT)
    {
      /* Adding to an address register or with another/same register
         is not possible. This must be replaced.  */
      if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
        return \"#\";

      return \"addd\\t%2\";
    }
  val = INTVAL (operands[2]);
  if (!SP_REG_P (operands[0]))
    {
      if (D_REG_P (operands[0]))
	{
	  if ((val & 0x0ff) == 0 && !next_insn_test_reg (insn, operands[0]))
	    {
	      CC_STATUS_INIT;
	      return \"adda\\t%h2\";
	    }
	  else
	    {
	      return \"addd\\t%2\";
	    }
	}
      else if (GET_CODE (operands[2]) != CONST_INT
	       || INTVAL (operands[2]) < -4
	       || INTVAL (operands[2]) > 4)
        return \"#\";
    }
  if (val > 0)
    {
      insn_code = X_REG_P (operands[0]) ? \"inx\"
		    : Y_REG_P (operands[0]) ? \"iny\" : \"ins\";
    }
  else
    {
      val  = -val;
      insn_code = X_REG_P (operands[0]) ? \"dex\"
		    : Y_REG_P (operands[0]) ? \"dey\" : \"des\";
    }

  /* For X and Y increment, the flags are not complete.  Only the Z flag
     is updated.  For SP increment, flags are not changed.  */
  if (SP_REG_P (operands[0]))
    {
      cc_status = cc_prev_status; 
      if (INTVAL (operands[2]) < 0)
	{
	  while (val >= 2)
	    {
	      output_asm_insn (\"pshx\", operands);
	      val -= 2;
	    }
	}
      else if (optimize && dead_register_here (insn, ix_reg))
	{
	  while (val >= 2)
	    {
	      output_asm_insn (\"pulx\", operands);
	      val -= 2;
	    }
	}
    }
  else
    {
      CC_STATUS_INIT;
    }

  while (val)
    {
      output_asm_insn (insn_code, operands);
      val--;
    }
  return \"\";
}")

(define_insn "*addhi3_zext"
  [(set (match_operand:HI 0 "hard_reg_operand" "=A,d")
	(plus:HI (zero_extend:HI 
		     (match_operand:QI 1 "nonimmediate_operand" "d,um*A"))
		 (match_operand:HI 2 "hard_reg_operand" "0,0")))]
  ""
  "*
{
  CC_STATUS_INIT;
  if (A_REG_P (operands[0]))
    return \"ab%0\";
  else if (A_REG_P (operands[1]))
    return \"st%1\\t%t0\\n\\taddb\\t%T0\\n\\tadca\\t#0\";
  else 
    return \"addb\\t%b1\\n\\tadca\\t#0\";
}")

;;
;; Translate d = d + d into d = << 1
;; We have to do this because adding a register to itself is not possible.
;; ??? It's not clear whether this is really necessary.
;;
(define_split
  [(set (match_operand:QI 0 "hard_reg_operand" "")
	(plus:QI (match_dup 0)
		 (match_dup 0)))]
  "0 && reload_completed"
  [(set (match_dup 0) (ashift:QI (match_dup 0) (const_int 1)))]
  "")

(define_insn "addqi3"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=!d*rm,dq,!*A")
        (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0")
                 (match_operand:QI 2 "general_operand" "N,ium*A*d,ium*A*d")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    {
      if (INTVAL (operands[2]) == 1)
	{
	  if (DA_REG_P (operands[0]))
	    {
	      return \"inca\";
	    }
	  else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
	   {
	     return \"incb\";

	   }
	  else if (A_REG_P (operands[0]))
	   {
	     /* This applies on the 16-bit register.  This should be ok since
	        this is not a strict_low_part increment.  */
	     return \"in%0\";
	   }
	  else
	   {
	     return \"inc\\t%b0\";
	   }
	}
      else if (INTVAL (operands[2]) == -1)
	{
	  if (DA_REG_P (operands[0]))
	    {
	      return \"deca\";
	    }
	  else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
	    {
	      return \"decb\";
	    }
	  else if (A_REG_P (operands[0]))
	    {
	     /* This applies on the 16-bit register.  This should be ok since
	        this is not a strict_low_part decrement.  */
	      return \"de%0\";
	    }
	  else
	    {
	      return \"dec\\t%b0\";
	    }
	}
    }
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";
  else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    return \"addb\\t%b2\";
  else
    return \"adda\\t%b2\";
}")

;;
;; add with carry is used for 32-bit add.
;;
(define_insn "*adcq"
  [(set (match_operand:QI 0 "register_operand" "=q")
        (plus:QI (plus:QI (reg:QI CC_REGNUM)
                          (match_operand:QI 1 "register_operand" "%0"))
                 (match_operand:QI 2 "general_operand" "ium")))]
  ""
  "adc%0\\t%b2")

;;--------------------------------------------------------------------
;;- Subtract instructions.
;;--------------------------------------------------------------------

(define_expand "subdi3"
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(minus:DI (match_operand:DI 1 "nonimmediate_operand" "")
		  (match_operand:DI 2 "general_operand" "")))]
  ""
  "m68hc11_emit_libcall (\"___subdi3\", MINUS, DImode, DImode, 3, operands);
   DONE;")

;;
;; 32-bit Subtract (see addsi3)
;; Subtract with a constant are handled by addsi3.
;;
;;
;; - 32-bit Add.
;;
(define_expand "subsi3"
  [(parallel [(set (match_operand:SI 0 "register_operand" "")
	             (minus:SI (match_operand:SI 1 "register_operand" "")
		              (match_operand:SI 2 "general_operand" "")))
              (clobber (match_scratch:HI 3 ""))])]
  ""
  "")

(define_insn "*subsi3"
  [(set (match_operand:SI 0 "register_operand" "=D,D,D,D,!u")
	(minus:SI (match_operand:SI 1 "general_operand" "0,oi,0,!u,0")
		  (match_operand:SI 2 "general_operand" "oi,D,!u,D,!oui")))
   (clobber (match_scratch:HI 3 "=X,X,X,X,d"))]
  ""
  "#")

(define_insn "*subsi3_zero_extendhi"
  [(set (match_operand:SI 0 "register_operand" "=D")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
	    (zero_extend:SI (match_operand:HI 2 "general_operand" "dmui*A"))))
   (clobber (match_scratch:HI 3 "=X"))]
  ""
  "*
{
  rtx ops[2];

  if (A_REG_P (operands[2]))
    {
      if (TARGET_M6812)
        ops[0] = gen_rtx (MEM, HImode,
			  gen_rtx (PRE_DEC, HImode,
				   gen_rtx (REG, HImode, HARD_SP_REGNUM)));
      else
        ops[0] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);

      ops[1] = operands[2];
      m68hc11_gen_movhi (insn, ops);
      if (TARGET_M6812)
        operands[2] = gen_rtx (MEM, HImode,
			       gen_rtx (POST_INC, HImode,
				        gen_rtx (REG, HImode, HARD_SP_REGNUM)));
      else
        operands[2] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
    }
  ops[0] = gen_label_rtx (); 
  output_asm_insn (\"subd\\t%2\", operands);
  output_asm_insn (\"bcc\\t%l0\", ops);
  output_asm_insn (\"dex\", ops);
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
  CC_STATUS_INIT;
  return \"\";
}")

(define_insn "*subsi3_zero_extendqi"
  [(set (match_operand:SI 0 "register_operand" "=D")
	(minus:SI (match_operand:SI 1 "register_operand" "0")
	    (zero_extend:SI (match_operand:QI 2 "general_operand" "dmui*A"))))
   (clobber (match_scratch:HI 3 "=X"))]
  ""
  "*
{
  rtx ops[2];

  if (A_REG_P (operands[2]))
    {
      ops[0] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
      ops[1] = operands[2];
      m68hc11_gen_movhi (insn, ops);
      operands[2] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);
    }
  ops[0] = gen_label_rtx (); 
  output_asm_insn (\"subb\\t%b2\", operands);
  output_asm_insn (\"sbca\\t#0\", operands);
  output_asm_insn (\"bcc\\t%l0\", ops);
  output_asm_insn (\"dex\", ops);
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
  CC_STATUS_INIT;
  return \"\";
}")

;;
;; reg:HI 1 -> d	reg:QI 6 -> B
;; reg:QI 7 -> ccr      reg:QI 5 -> A
;;
(define_split /* "*subsi3" */
  [(set (match_operand:SI 0 "register_operand" "")
	(minus:SI (match_operand:SI 1 "register_operand" "")
		  (match_operand:SI 2 "general_operand" "")))
   (clobber (match_scratch:HI 3 "=X"))]
  "reload_completed && z_replacement_completed == 2
   && X_REG_P (operands[1])"
  [(set (reg:HI D_REGNUM) (minus:HI (reg:HI D_REGNUM) (match_dup 3)))
   (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
              (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
   (set (reg:QI B_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
   (set (reg:QI A_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
   (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
              (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])]
  "operands[3] = m68hc11_gen_lowpart (HImode, operands[2]);
   operands[4] = m68hc11_gen_highpart (HImode, operands[2]);
   operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
   operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")

(define_split /* "*subsi3" */
  [(set (match_operand:SI 0 "register_operand" "")
	(minus:SI (match_operand:SI 1 "general_operand" "")
		  (match_operand:SI 2 "register_operand" "")))
   (clobber (match_scratch:HI 3 "=X"))]
  "reload_completed && z_replacement_completed == 2
   && X_REG_P (operands[2])"
  [(set (reg:HI D_REGNUM) (minus:HI (reg:HI D_REGNUM) (match_dup 3)))
   (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
              (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
   (set (reg:QI B_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI B_REGNUM)) (match_dup 4)))
   (set (reg:QI A_REGNUM) (minus:QI (minus:QI (reg:QI CC_REGNUM) (reg:QI A_REGNUM)) (match_dup 5)))
   (parallel [(set (reg:HI X_REGNUM) (reg:HI D_REGNUM))
              (set (reg:HI D_REGNUM) (reg:HI X_REGNUM))])
   (set (reg:SI 0) (neg:SI (reg:SI 0)))]
  "operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);
   operands[4] = m68hc11_gen_highpart (HImode, operands[1]);
   operands[5] = m68hc11_gen_highpart (QImode, operands[4]);
   operands[4] = m68hc11_gen_lowpart (QImode, operands[4]);")

(define_split /* "*subsi3" */
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(minus:SI (match_operand:SI 1 "general_operand" "")
		  (match_operand:SI 2 "general_operand" "")))
   (clobber (match_scratch:HI 3 "=d"))]
  "reload_completed && z_replacement_completed == 2
   && !X_REG_P (operands[0])"
  [(set (match_dup 3) (match_dup 4))
   (set (match_dup 3) (minus:HI (match_dup 3) (match_dup 5)))
   (set (match_dup 4) (match_dup 3))
   (set (match_dup 3) (match_dup 6))
   (set (reg:QI 6) (minus:QI (minus:QI (reg:QI 7) (reg:QI 6)) (match_dup 7)))
   (set (reg:QI 5) (minus:QI (minus:QI (reg:QI 7) (reg:QI 5)) (match_dup 8)))
   (set (match_dup 6) (match_dup 3))]
  "operands[4] = m68hc11_gen_lowpart (HImode, operands[1]);
   operands[5] = m68hc11_gen_lowpart (HImode, operands[2]);
   operands[6] = m68hc11_gen_highpart (HImode, operands[1]);
   operands[7] = m68hc11_gen_highpart (HImode, operands[2]);
   operands[8] = m68hc11_gen_highpart (QImode, operands[7]);
   operands[7] = m68hc11_gen_lowpart (QImode, operands[7]);")

;;
;; - 16-bit Subtract.
;;
(define_expand "subhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(minus:HI (match_operand:HI 1 "register_operand" "0")
		  (match_operand:HI 2 "general_operand" "g")))]
  ""
  "")

;;
;; Subtract from stack. This is better if we provide a pattern.
;;
(define_insn "*subhi3_sp"
  [(set (match_operand:HI 0 "stack_register_operand" "=w,w")
	(minus:HI (match_operand:HI 1 "register_operand" "0,0")
		  (match_operand:HI 2 "general_operand" "im*d,!u*A")))
   (clobber (match_scratch:HI 3 "=A*d,A*d"))]
  ""
  "*
{
  if (X_REG_P (operands[2]))
    {
      operands[2] = m68hc11_soft_tmp_reg;
      output_asm_insn (\"stx\\t%2\", operands);
    }
  else if (Y_REG_P (operands[2]))
    {
      operands[2] = m68hc11_soft_tmp_reg;
      output_asm_insn (\"sty\\t%2\", operands);
    }
  else if (D_REG_P (operands[2]))
    {
      operands[2] = m68hc11_soft_tmp_reg;
      output_asm_insn (\"std\\t%2\", operands);
    }

  if (D_REG_P (operands[3]))
    {
      int save_x;

      save_x = !dead_register_here (insn, ix_reg);
      if (save_x)
	output_asm_insn (\"xgdx\", operands);
      output_asm_insn (\"tsx\", operands);
      output_asm_insn (\"xgdx\", operands);
      output_asm_insn (\"subd\\t%2\", operands);
      output_asm_insn (\"xgdx\", operands);

      /* The status flags correspond to the addd. xgdx/y and tx/ys do not
         modify the flags.  */
      output_asm_insn (\"txs\", operands);
      if (save_x)
        return \"xgdx\";
      else
        return \"\";
    }

  /* Need to transfer to SP to X,Y and then to D register.
     Register X,Y is lost, this is specified by the (clobber) statement.  */
  output_asm_insn (\"ts%3\", operands);
  output_asm_insn (\"xgd%3\", operands);
  output_asm_insn (\"subd\\t%2\", operands);
  output_asm_insn (\"xgd%3\", operands);

   /* The status flags correspond to the addd. xgdx/y and tx/ys do not
      modify the flags.  */
  return \"t%3s\";
}")


(define_insn "*subhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,*A,d*A")
	(minus:HI (match_operand:HI 1 "register_operand" "0,0,0")
		  (match_operand:HI 2 "general_operand" "im*A*d,im*d*A,!u")))]
  ""
  "*
{
  /* Adding to an address register or with another/same register
     is not possible.  This must be replaced.  */
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  return \"subd\\t%2\";
}")

(define_insn "*subhi3_zext"
  [(set (match_operand:HI 0 "hard_reg_operand" "=d,d")
	(minus:HI (match_operand:HI 1 "hard_reg_operand" "0,0")
           (zero_extend:HI (match_operand:QI 2 "general_operand" "mi*A,!u"))))]
  ""
  "*
{
  CC_STATUS_INIT;
  if (A_REG_P (operands[2]))
    {
      rtx ops[2];

      ops[0] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);
      ops[1] = operands[2];
      m68hc11_gen_movqi (insn, ops);
      return \"subb\\t%T0\\n\\tsbca\\t#0\";
    }
  return \"subb\\t%b2\\n\\tsbca\\t#0\";
}")

(define_insn "subqi3"
  [(set (match_operand:QI 0 "hard_reg_operand" "=dq,!*x*y")
        (minus:QI (match_operand:QI 1 "hard_reg_operand" "0,0")
                  (match_operand:QI 2 "general_operand" "uim*A*d,uim*A*d")))]
  ""
  "*
{
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";
  else if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    return \"subb\\t%b2\";
  else
    return \"suba\\t%b2\";
}")

;;
;; subtract with carry is used for 32-bit subtract.
;;
(define_insn "*subcq"
  [(set (match_operand:QI 0 "register_operand" "=q")
        (minus:QI (minus:QI (reg:QI CC_REGNUM)
                            (match_operand:QI 1 "register_operand" "0"))
                  (match_operand:QI 2 "general_operand" "ium")))]
  ""
  "sbc%0\\t%b2")

;;--------------------------------------------------------------------
;;- Multiply instructions.
;;--------------------------------------------------------------------
;;
;; 32 and 64-bit multiply are handled by the library
;;

(define_expand "mulsi3"
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(mult:SI (match_operand:SI 1 "general_operand" "")
		 (match_operand:SI 2 "general_operand" "")))]
  ""
  "m68hc11_emit_libcall (\"__mulsi3\", MULT, SImode, SImode, 3, operands);
   DONE;")

(define_expand "mulhi3"
  [(parallel [(set (match_operand:HI 0 "register_operand" "")
		       (mult:HI (match_operand:HI 1 "register_operand" "")
			        (match_operand:HI 2 "register_operand" "")))
              (clobber (match_scratch:HI 3 ""))])]
  ""
  "")

(define_insn "mulhi3_m68hc11"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(mult:HI (match_operand:HI 1 "register_operand" "%0")
		 (match_operand:HI 2 "register_operand" "x")))
   (clobber (match_scratch:HI 3 "=X"))]
  "TARGET_M6811"
  "*
{
  CC_STATUS_INIT;
  /* D * X -> D  (X and Y are preserved by this function call).  */
  return \"jsr\\t___mulhi3\";
}")

(define_insn "mulhi3_m68hc12"
  [(set (match_operand:HI 0 "register_operand" "=d,d")
	(mult:HI (match_operand:HI 1 "register_operand" "%0,0")
		 (match_operand:HI 2 "register_operand" "y,x")))
   (clobber (match_scratch:HI 3 "=2,2"))]
  "TARGET_M6812"
  "*
{
  CC_STATUS_INIT;
  if (X_REG_P (operands[2]))
    return \"exg\\tx,y\\n\\temul\\n\\texg\\tx,y\";
  else
    return \"emul\";
}")

(define_insn "umulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=D,D")
        (mult:SI (zero_extend:SI
		     (match_operand:HI 1 "register_operand" "%d,d"))
		 (zero_extend:SI
	             (match_operand:HI 2 "register_operand" "y,x"))))
   (clobber (match_scratch:HI 3 "=2,X"))]
  "TARGET_M6812"
  "*
{
  if (X_REG_P (operands [2]))
    output_asm_insn (\"exg\\tx,y\", operands);

  /* Can't use the carry after that; other flags are ok when testing
     the 32-bit result.  */
  cc_status.flags |= CC_NO_OVERFLOW;
  return \"emul\\n\\texg\\tx,y\";
}")

(define_insn "mulhisi3"
  [(set (match_operand:SI 0 "register_operand" "=D,D")
        (mult:SI (sign_extend:SI
		     (match_operand:HI 1 "register_operand" "%d,d"))
		 (sign_extend:SI
	             (match_operand:HI 2 "register_operand" "y,x"))))
   (clobber (match_scratch:HI 3 "=2,X"))]
  "TARGET_M6812"
  "*
{
  if (X_REG_P (operands [2]))
    output_asm_insn (\"exg\\tx,y\", operands);

  /* Can't use the carry after that; other flags are ok when testing
     the 32-bit result.  */
  cc_status.flags |= CC_NO_OVERFLOW;
  return \"emuls\\n\\texg\\tx,y\";
}")

(define_insn "umulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=d")
        (mult:HI (zero_extend:HI
		     (match_operand:QI 1 "nonimmediate_operand" "dm*u"))
		 (zero_extend:HI
	             (match_operand:QI 2 "nonimmediate_operand" "dm*u*A"))))]
  ""
  "*
{
  if (D_REG_P (operands[1]) && D_REG_P (operands[2]))
    {
      output_asm_insn (\"tba\", operands);
    }
  else
    {
      rtx ops[2];

      if (D_REG_P (operands[2]))
	{
	  rtx temp = operands[2];
	  operands[2] = operands[1];
	  operands[1] = temp;
	}

      ops[0] = gen_rtx (REG, QImode, HARD_A_REGNUM);
      ops[1] = operands[2];
      m68hc11_gen_movqi (insn, ops);

      if (!D_REG_P (operands[1]))
	{
	  output_asm_insn (\"ldab\\t%b1\", operands);
	}
    }

  CC_STATUS_INIT;
  return \"mul\";
}")

(define_insn "mulqi3"
  [(set (match_operand:QI 0 "register_operand" "=d,*x,*y")
        (mult:QI (match_operand:QI 1 "nonimmediate_operand" "%dum,0,0")
		 (match_operand:QI 2 "general_operand" "dium,*xium,*yium")))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  if (D_REG_P (operands[1]) && D_REG_P (operands[2]))
    {
      output_asm_insn (\"tba\", operands);
    }
  else
    {
      if (D_REG_P (operands[2]))
	{
	  rtx temp = operands[2];
	  operands[2] = operands[1];
	  operands[1] = temp;
	}
	
      output_asm_insn (\"ldaa\\t%b2\", operands);

      if (!D_REG_P (operands[1]))
	{
	  output_asm_insn (\"ldab\\t%b1\", operands);
	}
    }

  CC_STATUS_INIT;
  return \"mul\";
}")

(define_split
  [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
        (mult:QI (match_operand:QI 1 "general_operand" "")
		 (match_operand:QI 2 "general_operand" "")))]
  "z_replacement_completed == 2"
  [(parallel [(set (reg:HI D_REGNUM) (match_dup 3))
	      (set (match_dup 3) (reg:HI D_REGNUM))])
   (set (reg:QI D_REGNUM) (mult:QI (match_dup 5) (match_dup 6)))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
              (set (match_dup 3) (reg:HI D_REGNUM))])]
  "
   operands[3] = gen_rtx (REG, HImode, REGNO (operands[0]));
   if (A_REG_P (operands[1]))
     operands[5] = gen_rtx (REG, QImode, HARD_D_REGNUM);
   else
     operands[5] = operands[1];
   if (A_REG_P (operands[2]))
     operands[6] = gen_rtx (REG, QImode, HARD_D_REGNUM);
   else
     operands[6] = operands[2];
  ")

(define_insn "mulqihi3"
  [(set (match_operand:HI 0 "register_operand" "=d,d")
        (mult:HI (sign_extend:HI
			(match_operand:QI 1 "register_operand" "%0,0"))
		 (sign_extend:HI
                        (match_operand:QI 2 "nonimmediate_operand" "dm,*A"))))]
  ""
  "*
{
  CC_STATUS_INIT;

  /* Special case when multiplying the register with itself.  */
  if (D_REG_P (operands[2]))
    {
      output_asm_insn (\"tba\", operands);
      return \"mul\";
    }

  if (!H_REG_P (operands[2]))
    {
      output_asm_insn (\"ldaa\\t%b2\", operands);
    }
  else
    {
      rtx ops[2];

      ops[0] = gen_rtx (REG, QImode, HARD_A_REGNUM);
      ops[1] = operands[2];
      m68hc11_gen_movqi (insn, ops);
    }
  return \"jsr\\t___mulqi3\";
}")

;;--------------------------------------------------------------------
;;- Divide instructions.
;;--------------------------------------------------------------------

(define_insn "divmodhi4"
  [(set (match_operand:HI 0 "register_operand" "=d,d")
          (div:HI (match_operand:HI 1 "register_operand" "0,0")
	          (match_operand:HI 2 "general_operand" "A,ium")))
   (set (match_operand:HI 3 "register_operand" "=&x,&x")
	(mod:HI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  if (!X_REG_P (operands[2])) 
    {
      if (Y_REG_P (operands[2]))
	{
	  output_asm_insn (\"sty\\t%t1\", operands);
	  output_asm_insn (\"ldx\\t%t1\", operands);
	}
      else
	{
          output_asm_insn (\"ldx\\t%2\", operands);
	}
    }
  if (TARGET_M6812)
    {
      /* Flags are ok after that.  */
      return \"idivs\\n\\txgdx\";      
    }
  else
    {
      CC_STATUS_INIT;
      return \"bsr\\t__divmodhi4\";
    }
}")

(define_insn "udivmodhi4"
  [(set (match_operand:HI 0 "register_operand" "=d,d")
          (udiv:HI (match_operand:HI 1 "register_operand" "0,0")
	           (match_operand:HI 2 "general_operand" "A,ium")))
   (set (match_operand:HI 3 "register_operand" "=x,x")
	(umod:HI (match_dup 1) (match_dup 2)))]
  ""
  "*
{
  if (!X_REG_P (operands[2])) 
    {
      if (Y_REG_P (operands[2]))
	{
	  output_asm_insn (\"sty\\t%t1\", operands);
	  output_asm_insn (\"ldx\\t%t1\", operands);
	}
      else
	{
          output_asm_insn (\"ldx\\t%2\", operands);
	}
    }

  /* Z V and C flags are set but N is unchanged.
     Since this is an unsigned divide, we can probably keep the flags
     and indicate this.  */
  cc_status.flags |= CC_NOT_NEGATIVE;
  return \"idiv\\n\\txgdx\";
}")

;;--------------------------------------------------------------------
;;- and instructions.
;;--------------------------------------------------------------------

(define_insn "anddi3"
  [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
	(and:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
		(match_operand:DI 2 "general_operand" "imu,imu")))
   (clobber (match_scratch:HI 3 "=d,d"))]
  ""
  "#")

(define_insn "andsi3"
  [(set (match_operand:SI 0 "register_operand" "=D,!u")
	(and:SI (match_operand:SI 1 "register_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "Dimu,imu")))
   (clobber (match_scratch:HI 3 "=X,d"))]
  ""
  "#")

(define_expand "andhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(and:HI (match_operand:HI 1 "register_operand" "")
		(match_operand:HI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*andhi3_mem"
  [(set (match_operand:HI 0 "memory_operand" "=Q,R")
	(and:HI (match_dup 0)
	        (match_operand:HI 1 "immediate_operand" "i,i")))
   (clobber (match_scratch:HI 2 "=xy,X"))]
  "TARGET_RELAX && !TARGET_M6812"
  "*
{
  int val = INTVAL (operands[1]) & 0x0FFFF;

  if (val == 0x0ffff)
    {
      cc_status = cc_prev_status;
      return \"\";
    }

  CC_STATUS_INIT;

  /* The bclr instruction uses an inverted mask.  */
  operands[1] = GEN_INT ((~val) & 0x0FFFF);

  /* When destination is a global variable, generate a .relax instruction
     and load the address in the clobber register.  That load can be
     eliminated by the linker if the address is in page0.  */
  if (which_alternative == 0)
    {
      rtx ops[3];

      ops[0] = operands[2];
      ops[1] = XEXP (operands[0], 0);
      ops[2] = gen_label_rtx ();
      output_asm_insn (\".relax\\t%l2\", ops);
      m68hc11_gen_movhi (insn, ops);
      if ((val & 0x0FF) != 0x0FF)
        output_asm_insn (\"bclr\\t1,%2, %b1\", operands);

      if ((val & 0x0FF00) != 0x0FF00)
        output_asm_insn (\"bclr\\t0,%2, %h1\", operands);

      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[2]));
      return \"\";
    }

  if ((val & 0x0FF) != 0x0FF)
    output_asm_insn (\"bclr\\t%b0, %b1\", operands);

  if ((val & 0x0FF00) != 0x0FF00)
    output_asm_insn (\"bclr\\t%h0, %h1\", operands);

  return \"\";
}")

(define_insn "*andhi3_const"
  [(set (match_operand:HI 0 "reg_or_some_mem_operand" "=R,d,?*A")
	(and:HI (match_operand:HI 1 "reg_or_some_mem_operand" "%0,0,0")
	        (match_operand:HI 2 "const_int_operand" "")))]
  ""
  "*
{
  int val = INTVAL (operands[2]) & 0x0FFFF;
  int lowpart_zero = 0;
  int highpart_zero = 0;
  int lowpart_unknown = 0;
  int highpart_unknown = 0;

  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (val == 0x0ffff)
    {
      cc_status = cc_prev_status;
      return \"\";
    }

  /* First, try to clear the low and high part.
     If that's possible, the second 'and' will give
     the good status flags and we can avoid a tsthi.  */
  if ((val & 0x0FF) == 0)
    {
      if (D_REG_P (operands[0]))
	output_asm_insn (\"clrb\", operands);
      else
	output_asm_insn (\"clr\\t%b0\", operands);
      lowpart_zero = 1;
    }
  if ((val & 0x0FF00) == 0)
    {
      if (D_REG_P (operands[0]))
	output_asm_insn (\"clra\", operands);
      else
	output_asm_insn (\"clr\\t%h0\", operands);
      highpart_zero = 1;
    }

  if ((val & 0x0FF) == 0x0FF)
    {
      lowpart_unknown = 1;
    }
  else if ((val & 0x0FF) != 0 && !H_REG_P (operands[0]))
    {
      rtx ops[2];

      ops[0] = operands[0];
      ops[1] = GEN_INT ((~val) & 0x0FF);
      output_asm_insn (\"bclr\\t%b0, %1\", ops);
    }
  else if ((val & 0x0FF) != 0)
    {
      output_asm_insn (\"andb\\t%b2\", operands);
    }

  if ((val & 0x0FF00) == 0x0FF00)
    {
      highpart_unknown = 1;
    }
  else if (((val & 0x0FF00) != 0) && !H_REG_P (operands[0]))
    {
      rtx ops[2];

      ops[0] = operands[0];
      ops[1] = GEN_INT (((~val) & 0x0FF00) >> 8);
      output_asm_insn (\"bclr\\t%h0, %1\", ops);
    }
  else if ((val & 0x0FF00) != 0)
    {
      output_asm_insn (\"anda\\t%h2\", operands);
    }

  if (highpart_unknown || lowpart_unknown)
     CC_STATUS_INIT;
  else if (highpart_zero == 0 && lowpart_zero == 0)
     CC_STATUS_INIT;

  return \"\";
}")

(define_insn "*andhi3_gen"
  [(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
	(and:HI (match_operand:HI 1 "register_operand" "%0,0,0")
		(match_operand:HI 2 "general_operand" "mi,!u*A,!um*A")))]
  ""
  "*
{
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  CC_STATUS_INIT;
  return \"anda\\t%h2\\n\\tandb\\t%b2\";
}")

(define_expand "andqi3"
  [(set (match_operand:QI 0 "register_operand" "")
        (and:QI (match_operand:QI 1 "register_operand" "")
                (match_operand:QI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*andqi3_mem"
  [(set (match_operand:QI 0 "memory_operand" "=Q,R")
	(and:QI (match_dup 0)
	        (match_operand:QI 1 "const_int_operand" "i,i")))
   (clobber (match_scratch:HI 2 "=xy,X"))]
  "TARGET_RELAX && !TARGET_M6812"
  "*
{
  int val = INTVAL (operands[1]) & 0x0FF;

  if (val == 0x0ff)
    {
      cc_status = cc_prev_status;
      return \"\";
    }

  /* The bclr instruction uses an inverted mask.  */
  operands[1] = GEN_INT ((~val) & 0x0FF);

  /* When destination is a global variable, generate a .relax instruction
     and load the address in the clobber register.  That load can be
     eliminated by the linker if the address is in page0.  */
  if (which_alternative == 0)
    {
      rtx ops[3];

      ops[0] = operands[2];
      ops[1] = XEXP (operands[0], 0);
      ops[2] = gen_label_rtx ();
      output_asm_insn (\".relax\\t%l2\", ops);
      m68hc11_gen_movhi (insn, ops);
      output_asm_insn (\"bclr\\t0,%2, %1\", operands);
      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[2]));
      return \"\";
    }
  return \"bclr\\t%b0, %1\";
}")

(define_insn "*andqi3_const"
  [(set (match_operand:QI 0 "reg_or_some_mem_operand" "=R,d,?*A*q")
	(and:QI (match_operand:QI 1 "reg_or_some_mem_operand" "%0,0,0")
	        (match_operand:QI 2 "const_int_operand" "")))]
  ""
  "*
{
  int val = INTVAL (operands[2]) & 0x0FF;

  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (val == 0x0ff)
    {
      cc_status = cc_prev_status;
      return \"\";
    }
  if (!H_REG_P (operands[0]))
    {
      rtx ops[2];

      ops[0] = operands[0];
      ops[1] = GEN_INT ((~val) & 0x0FF);
      output_asm_insn (\"bclr\\t%b0, %b1\", ops);
      return \"\";
    }
  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    return \"andb\\t%b2\";
  else if (DA_REG_P (operands[0]))
    return \"anda\\t%b2\";
  else
    fatal_insn (\"Invalid operand in the instruction\", insn);
}")

(define_insn "*andqi3_gen"
  [(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
        (and:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,0")
             (match_operand:QI 2 "general_operand" "mi,!u,?*A,!um,?*A*d,!um*A")))]
  ""
  "*
{
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    return \"andb\\t%b2\";
  else if (DA_REG_P (operands[0]))
    return \"anda\\t%b2\";
  else
    fatal_insn (\"Invalid operand in the instruction\", insn);
}")

;;--------------------------------------------------------------------
;;- Bit set or instructions.
;;--------------------------------------------------------------------

(define_insn "iordi3"
  [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
	(ior:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
		(match_operand:DI 2 "general_operand" "imu,imu")))
   (clobber (match_scratch:HI 3 "=d,d"))]
  ""
  "#")

(define_insn "iorsi3"
  [(set (match_operand:SI 0 "register_operand" "=D,!u")
	(ior:SI (match_operand:SI 1 "register_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "Dimu,imu")))
   (clobber (match_scratch:HI 3 "=X,d"))]
  ""
  "#")

(define_expand "iorhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(ior:HI (match_operand:HI 1 "register_operand" "")
		(match_operand:HI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*iorhi3_mem"
  [(set (match_operand:HI 0 "memory_operand" "=Q,R")
	(ior:HI (match_dup 0)
	        (match_operand:HI 1 "const_int_operand" "")))
   (clobber (match_scratch:HI 2 "=xy,X"))]
  "TARGET_RELAX && !TARGET_M6812"
  "*
{
  int val = INTVAL (operands[1]) & 0x0FFFF;

  if (val == 0)
    {
      cc_status = cc_prev_status;
      return \"\";
    }
  CC_STATUS_INIT;
  if (which_alternative == 0)
    {
      rtx ops[3];

      ops[0] = operands[2];
      ops[1] = XEXP (operands[0], 0);
      ops[2] = gen_label_rtx ();
      output_asm_insn (\".relax\\t%l2\", ops);
      m68hc11_gen_movhi (insn, ops);
      if ((val & 0x0FF) != 0)
        output_asm_insn (\"bset\\t1,%2, %b1\", operands);

      if ((val & 0x0FF00) != 0)
        output_asm_insn (\"bset\\t0,%2, %h1\", operands);
      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[2]));
      return \"\";
    }

  if ((val & 0x0FF) != 0)
    output_asm_insn (\"bset\\t%b0, %b1\", operands);

  if ((val & 0x0FF00) != 0)
    output_asm_insn (\"bset\\t%h0, %h1\", operands);

  return \"\";
}")

(define_insn "*iorhi3_const"
  [(set (match_operand:HI 0 "reg_or_some_mem_operand" "=R,d,?*A")
	(ior:HI (match_operand:HI 1 "reg_or_some_mem_operand" "%0,0,0")
		(match_operand:HI 2 "const_int_operand" "")))]
  ""
  "*
{
  int val = INTVAL (operands[2]) & 0x0FFFF;

  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (val == 0)
    {
      cc_status = cc_prev_status;
      return \"\";
    }

  if ((val & 0x0FF) != 0)
    {
      if (!H_REG_P (operands[0]))
        output_asm_insn (\"bset\\t%b0, %b2\", operands);
      else
	output_asm_insn (\"orab\\t%b2\", operands);
    }

  if ((val & 0x0FF00) != 0)
    {
      if (!H_REG_P (operands[0]))
         output_asm_insn (\"bset\\t%h0, %h2\", operands);
      else
	 output_asm_insn (\"oraa\\t%h2\", operands);
    }

  CC_STATUS_INIT;
  return \"\";
}")

(define_insn "*iorhi3_gen"
  [(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
	(ior:HI (match_operand:HI 1 "register_operand" "%0,0,0")
		(match_operand:HI 2 "general_operand" "mi,!u*A,!um*A")))]
  ""
  "*
{
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  CC_STATUS_INIT;
  return \"oraa\\t%h2\\n\\torab\\t%b2\";
}")

(define_expand "iorqi3"
  [(set (match_operand:QI 0 "register_operand" "")
	(ior:QI (match_operand:QI 1 "register_operand" "")
	        (match_operand:QI 2 "general_operand" "")))]
  ""
  "")

(define_insn "*iorqi3_mem"
  [(set (match_operand:QI 0 "memory_operand" "=Q,R")
	(ior:QI (match_dup 0)
	        (match_operand:QI 1 "const_int_operand" "")))
   (clobber (match_scratch:HI 2 "=xy,X"))]
  "TARGET_RELAX && !TARGET_M6812"
  "*
{
  int val = INTVAL (operands[1]) & 0x0FF;

  if (val == 0)
    {
      cc_status = cc_prev_status;
      return \"\";
    }
  if (which_alternative == 0)
    {
      rtx ops[3];

      ops[0] = operands[2];
      ops[1] = XEXP (operands[0], 0);
      ops[2] = gen_label_rtx ();
      output_asm_insn (\".relax\\t%l2\", ops);
      m68hc11_gen_movhi (insn, ops);
      output_asm_insn (\"bset\\t0,%2, %1\", operands);
      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[2]));
      return \"\";
    }
  return \"bset\\t%b0, %1\";
}")

(define_insn "*iorqi3_const"
  [(set (match_operand:QI 0 "reg_or_some_mem_operand" "=R,d,?*A*q")
	(ior:QI (match_operand:QI 1 "reg_or_some_mem_operand" "%0,0,0")
		(match_operand:QI 2 "const_int_operand" "")))]
  ""
  "*
{
  int val = INTVAL (operands[2]) & 0x0FF;

  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (val == 0)
    {
      cc_status = cc_prev_status;
      return \"\";
    }
  if (!H_REG_P (operands[0]))
    {
      return \"bset\\t%b0, %2\";
    }

  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    return \"orab\\t%b2\";
  else if (DA_REG_P (operands[0]))
    return \"oraa\\t%b2\";
  else
    fatal_insn (\"Invalid operand in the instruction\", insn);
}")

(define_insn "*iorqi3_gen"
  [(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
	(ior:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,0")
	     (match_operand:QI 2 "general_operand" "mi,!u,!*A,!um,?*A*d,!um*A")))]
  ""
  "*
{
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    return \"orab\\t%b2\";
  else if (DA_REG_P (operands[0]))
    return \"oraa\\t%b2\";
  else
    fatal_insn (\"Invalid operand in the instruction\", insn);
}")


;;--------------------------------------------------------------------
;;- xor instructions.
;;--------------------------------------------------------------------

(define_insn "xordi3"
  [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=m,u")
	(xor:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu,imu")
		(match_operand:DI 2 "general_operand" "imu,imu")))
   (clobber (match_scratch:HI 3 "=d,d"))]
  ""
  "#")

(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=D,!u")
	(xor:SI (match_operand:SI 1 "register_operand" "%0,0")
		(match_operand:SI 2 "general_operand" "Dimu,imu")))
   (clobber (match_scratch:HI 3 "=X,d"))]
  ""
  "#")

(define_insn "xorhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,d,!*A")
	(xor:HI (match_operand:HI 1 "register_operand" "%0,0,0")
		(match_operand:HI 2 "general_operand" "im,!u*A,!ium*A")))]
  ""
  "*
{
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (GET_CODE (operands[2]) == CONST_INT)
    {
      int val = INTVAL (operands[2]) & 0x0FFFF;

      if (val == 0)
	{
	  cc_status = cc_prev_status;
	  return \"\";
	}
      if ((val & 0x0FF) != 0)
	{
	  output_asm_insn (\"eorb\\t%b2\", operands);
	}
      else if ((val & 0x0FF) == 0x0FF)
	{
	  output_asm_insn (\"comb\", operands);
	}

      if ((val & 0x0FF00) != 0)
	{
	  output_asm_insn (\"eora\\t%h2\", operands);
	}
      else if ((val & 0x0FF00) == 0x0FF00)
	{
	  output_asm_insn (\"coma\", operands);
	}

      CC_STATUS_INIT;
      return \"\";
    }

  CC_STATUS_INIT;
  return \"eora\\t%h2\\n\\teorb\\t%b2\";
}")

(define_insn "xorqi3"
  [(set (match_operand:QI 0 "register_operand" "=d,d,d,?*A,?*A,!*q")
        (xor:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,0")
             (match_operand:QI 2 "general_operand" "im,!u,!*A,!ium,?*A*d,!ium*A")))]
  ""
  "*
{
  if (A_REG_P (operands[0]) || H_REG_P (operands[2]))
    return \"#\";

  if (GET_CODE (operands[2]) == CONST_INT)
    {
      int val = INTVAL (operands[2]) & 0x0FF;

      if (val == 0)
	{
	  cc_status = cc_prev_status;
	  return \"\";
	}
      if (val == 0x0FF)
	{
	  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
	    return \"comb\";
	  else
	    return \"coma\";
	}
    }
  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    return \"eorb\\t%b2\";
  else if (DA_REG_P (operands[0]))
    return \"eora\\t%b2\";
  else
    fatal_insn (\"Invalid operand in the instruction\", insn);
}")

;;--------------------------------------------------------------------
;;- Bit set or instructions.
;;--------------------------------------------------------------------

(define_insn "*logicalsi3_zexthi"
  [(set (match_operand:SI 0 "register_operand" "=D")
	(match_operator:SI 3 "m68hc11_logical_operator"
		[(zero_extend:SI
		     (match_operand:HI 1 "general_operand" "imudA"))
		 (match_operand:SI 2 "general_operand" "Dimu")]))]
  ""
  "#")

(define_insn "*logicalsi3_zextqi"
  [(set (match_operand:SI 0 "register_operand" "=D,D,D")
	(match_operator:SI 3 "m68hc11_logical_operator"
		[(zero_extend:SI
		     (match_operand:QI 1 "general_operand" "d,*A,imu"))
		 (match_operand:SI 2 "general_operand" "imu,imu,0")]))]
  ""
  "#")

(define_split /* logicalsi3_zextqi */
  [(set (match_operand:SI 0 "register_operand" "")
	(match_operator:SI 3 "m68hc11_logical_operator"
	         [(zero_extend:SI
		     (match_operand:QI 1 "general_operand" ""))
		  (match_operand:SI 2 "general_operand" "")]))]
  "z_replacement_completed == 2"
  [(set (reg:QI A_REGNUM) (match_dup 4))
   (set (reg:QI D_REGNUM) (match_dup 7))
   (set (reg:QI B_REGNUM) (match_op_dup 3 [(reg:QI B_REGNUM) (match_dup 5)]))
   (set (reg:HI X_REGNUM) (match_dup 6))]
  "PUT_MODE (operands[3], QImode);
   if (X_REG_P (operands[2]))
     {
       operands[5] = operands[1];
       /* Make all the (set (REG:x) (REG:y)) a nop set.  */
       operands[4] = gen_rtx (REG, QImode, HARD_A_REGNUM);
       operands[7] = gen_rtx (REG, QImode, HARD_D_REGNUM);
       operands[6] = gen_rtx (REG, HImode, HARD_X_REGNUM);
     }
   else
     {
       operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
       operands[7] = operands[1];
       operands[5] = m68hc11_gen_lowpart (QImode, operands[4]);
       operands[4] = m68hc11_gen_highpart (QImode, operands[4]);
       operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
     }
   /* For an AND, make sure the high 24-bit part is cleared.  */
   if (GET_CODE (operands[3]) == AND)
     {
       operands[4] = const0_rtx;
       operands[6] = const0_rtx;
     }
   ")

(define_split /* logicalsi3_zexthi */
  [(set (match_operand:SI 0 "register_operand" "")
	(match_operator:SI 3 "m68hc11_logical_operator"
	         [(zero_extend:SI
		     (match_operand:HI 1 "general_operand" ""))
		  (match_operand:SI 2 "general_operand" "")]))]
  "reload_completed"
  [(set (reg:HI D_REGNUM) (match_dup 4))
   (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 5)]))
   (set (reg:HI X_REGNUM) (match_dup 6))]
  "PUT_MODE (operands[3], HImode);
   if (X_REG_P (operands[2]))
     {
       operands[5] = operands[1];
       /* Make all the (set (REG:x) (REG:y)) a nop set.  */
       operands[4] = gen_rtx (REG, HImode, HARD_D_REGNUM);
       operands[6] = gen_rtx (REG, HImode, HARD_X_REGNUM);
     }
   else
     {
       operands[4] = operands[1];
       operands[5] = m68hc11_gen_lowpart (HImode, operands[2]);
       operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
     }
   /* For an AND, make sure the high 16-bit part is cleared.  */
   if (GET_CODE (operands[3]) == AND)
     {
       operands[6] = const0_rtx;
     }
   ")

(define_insn "*logicalhi3_zexthi_ashift8"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(match_operator:HI 3 "m68hc11_logical_operator"
		[(zero_extend:HI
		     (match_operand:QI 1 "general_operand" "imud"))
		 (ashift:HI
		     (match_operand:HI 2 "general_operand" "dimu")
		     (const_int 8))]))]
  ""
  "#")

(define_insn "*logicalhi3_zexthi"
  [(set (match_operand:HI 0 "register_operand" "=d,d")
	(match_operator:HI 3 "m68hc11_logical_operator"
		[(zero_extend:HI
		     (match_operand:QI 1 "general_operand" "imd*A,?u"))
		 (match_operand:HI 2 "general_operand" "dim,?dimu")]))]
  ""
  "#")

(define_split /* logicalhi3_zexthi */
  [(set (match_operand:HI 0 "register_operand" "")
	(match_operator:HI 3 "m68hc11_logical_operator"
		[(zero_extend:HI
		     (match_operand:QI 1 "general_operand" ""))
		 (match_operand:HI 2 "general_operand" "")]))]
  "z_replacement_completed == 2"
  [(set (reg:QI B_REGNUM) (match_dup 6))
   (set (reg:QI A_REGNUM) (match_dup 4))
   (set (reg:QI B_REGNUM) (match_op_dup 3 [(reg:QI B_REGNUM) (match_dup 5)]))]
  "
   PUT_MODE (operands[3], QImode);
   if (D_REG_P (operands[2]))
     {
       operands[4] = gen_rtx (REG, QImode, HARD_A_REGNUM);
       operands[5] = operands[1];
       operands[6] = gen_rtx (REG, QImode, HARD_B_REGNUM);
     }
   else
     {
       operands[4] = m68hc11_gen_highpart (QImode, operands[2]);
       operands[5] = m68hc11_gen_lowpart (QImode, operands[2]);
       if (D_REG_P (operands[1]))
	 operands[6] = gen_rtx (REG, QImode, HARD_B_REGNUM);
       else
         operands[6] = operands[1];
     }
   /* For an AND, make sure the high 8-bit part is cleared.  */
   if (GET_CODE (operands[3]) == AND)
     {
       operands[4] = const0_rtx;
     }
  ")

(define_split /* logicalhi3_zexthi_ashift8 */
  [(set (match_operand:HI 0 "register_operand" "")
	(match_operator:HI 3 "m68hc11_logical_operator"
		[(zero_extend:HI
		     (match_operand:QI 1 "general_operand" ""))
		 (ashift:HI
		     (match_operand:HI 2 "general_operand" "")
		     (const_int 8))]))]
  "z_replacement_completed == 2"
  [(set (reg:QI A_REGNUM) (match_dup 4))
   (set (reg:QI B_REGNUM) (match_dup 5))]
  "
   if (GET_CODE (operands[3]) == AND)
     {
       emit_insn (gen_movhi (operands[0], const0_rtx));
       DONE;
     }
   else
     {
       operands[5] = operands[1];
       if (D_REG_P (operands[2]))
         {
           operands[4] = gen_rtx (REG, QImode, HARD_B_REGNUM);
         }
       else
         {
           operands[4] = m68hc11_gen_lowpart (QImode, operands[2]);
         }
     }
  ")

(define_insn "*logicalsi3_silshr16"
  [(set (match_operand:SI 0 "register_operand" "=D,D,D")
          (match_operator:SI 3 "m68hc11_logical_operator"
	      [(lshiftrt:SI 
		   (match_operand:SI 1 "general_operand" "uim,uim,?D")
		   (const_int 16))
		(match_operand:SI 2 "general_operand" "uim,0,0")]))]
  ""
  "#")

(define_split /* logicalsi3_silshr16 */
  [(set (match_operand:SI 0 "register_operand" "")
          (match_operator:SI 3 "m68hc11_logical_operator"
		[(lshiftrt:SI 
			(match_operand:SI 1 "general_operand" "")
			(const_int 16))
		 (match_operand:SI 2 "general_operand" "")]))]
  "reload_completed"
  [(set (reg:HI D_REGNUM) (match_dup 4))
   (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 5)]))
   (set (reg:HI X_REGNUM) (match_dup 6))]
  "operands[5] = m68hc11_gen_highpart (HImode, operands[1]);
   if (X_REG_P (operands[2]))
     {
       operands[4] = gen_rtx (REG, HImode, HARD_D_REGNUM);
       operands[6] = gen_rtx (REG, HImode, HARD_X_REGNUM);
     }
   else
     {
       operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
       operands[6] = m68hc11_gen_highpart (HImode, operands[2]);
     }
   PUT_MODE (operands[3], HImode);

   /* For an AND, make sure the high 16-bit part is cleared.  */
   if (GET_CODE (operands[3]) == AND)
     {
       operands[6] = const0_rtx;
     }
")

(define_insn "*logicalsi3_silshl16"
  [(set (match_operand:SI 0 "register_operand" "=D,D")
          (match_operator:SI 3 "m68hc11_logical_operator"
	      [(ashift:SI 
		   (match_operand:SI 1 "general_operand" "uim,?D")
		   (const_int 16))
		(match_operand:SI 2 "general_operand" "0,0")]))]
  ""
  "#")

(define_split /* logicalsi3_silshl16 */
  [(set (match_operand:SI 0 "register_operand" "")
          (match_operator:SI 3 "m68hc11_logical_operator"
		[(ashift:SI 
			(match_operand:SI 1 "general_operand" "")
			(const_int 16))
		 (match_operand:SI 2 "general_operand" "")]))]
  "z_replacement_completed == 2"
  [(set (reg:HI X_REGNUM) (match_op_dup 3 [(reg:HI X_REGNUM) (match_dup 4)]))
   (set (reg:HI D_REGNUM) (match_dup 5))]
  "operands[4] = m68hc11_gen_lowpart (HImode, operands[1]);
   PUT_MODE (operands[3], HImode);

   if (GET_CODE (operands[3]) == AND)
     operands[5] = const0_rtx;
   else
     operands[5] = gen_rtx (REG, HImode, HARD_D_REGNUM);
   ")


;;--------------------------------------------------------------------
;;- 64/32-bit Logical Operations.  Patterns are defined so that GCC
;; can optimize correctly.  These insns are split by the `final'
;; pass (# pattern).  They are split to fall in the corresponding
;; 16-bit logical patterns.
;;--------------------------------------------------------------------

;; Split 64-bit logical operations: anddi3, iordi3, xordi3
(define_split
  [(set (match_operand:DI 0 "reg_or_some_mem_operand" "")
	(match_operator:DI 4 "m68hc11_logical_operator"
	     [(match_operand:DI 1 "reg_or_some_mem_operand" "")
	      (match_operand:DI 2 "general_operand" "")]))
   (clobber (match_scratch:HI 3 ""))]
  "reload_completed"
  [(const_int 0)]
  "m68hc11_split_logical (SImode, GET_CODE (operands[4]), operands);
   DONE;")

;; Split 32-bit logical operations: andsi3, iorsi3, xorsi3
(define_split
  [(set (match_operand:SI 0 "register_operand" "")
	(match_operator:SI 3 "m68hc11_logical_operator"
	     [(match_operand:SI 1 "register_operand" "")
	      (match_operand:SI 2 "general_operand" "")]))]
  "0 && reload_completed"
  [(const_int 0)]
  "m68hc11_split_logical (HImode, GET_CODE (operands[3]), operands);
   DONE;")

;; Split 32-bit logical operations: andsi3, iorsi3, xorsi3
(define_split
  [(set (match_operand:SI 0 "reg_or_some_mem_operand" "")
	(match_operator:SI 4 "m68hc11_logical_operator"
	     [(match_operand:SI 1 "reg_or_some_mem_operand" "")
	      (match_operand:SI 2 "general_operand" "")]))
   (clobber (match_scratch:HI 3 ""))]
  "reload_completed"
  [(const_int 0)]
  "m68hc11_split_logical (HImode, GET_CODE (operands[4]), operands);
   DONE;")

;;--------------------------------------------------------------------
;; 16-bit Arithmetic and logical operations on X and Y:
;;
;;	PLUS MINUS AND IOR XOR ASHIFT ASHIFTRT LSHIFTRT ROTATE ROTATERT
;;
;; Operations on X or Y registers are split here.  Instructions are
;; changed into:
;;   - xgdx/xgdy instruction pattern,
;;   - The same operation on register D,
;;   - xgdx/xgdy instruction pattern.
;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
;; We also handle the case were the address register is used in both source
;; operands, such as:
;;
;;     (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
;; or
;;     (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
;;
;;
(define_split
  [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
	(match_operator:HI 3 "m68hc11_arith_operator"
            [(match_operand:HI 1 "hard_addr_reg_operand" "")
	     (match_operand:HI 2 "general_operand" "")]))]
  "z_replacement_completed == 2
   /* If we are adding a small constant to X or Y, it's
     better to use one or several inx/iny instructions.  */
   && !(GET_CODE (operands[3]) == PLUS 
        && ((TARGET_M6812 
	     && (immediate_operand (operands[2], HImode)
		 || hard_reg_operand (operands[2], HImode)))
            || (GET_CODE (operands[2]) == CONST_INT
	        && INTVAL (operands[2]) >= -4
	        && INTVAL (operands[2]) <= 4)))"
  [(set (match_dup 9) (match_dup 0))
   (set (match_dup 4) (match_dup 5))
   (set (match_dup 8) (match_dup 7))
   (set (match_dup 0) (match_dup 1))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 6)]))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  "
   operands[9] = operands[0];
   /* For 68HC12, push the value on the stack and do the operation
      with a pop.  */
   if (TARGET_M6812
       && m68hc11_non_shift_operator (operands[3], HImode)
       && (H_REG_P (operands[2])
	   || (m68hc11_small_indexed_indirect_p (operands[2], HImode)
	       && reg_mentioned_p (operands[0], operands[2]))))
     {
       operands[4] = gen_rtx (MEM, HImode,
			      gen_rtx (PRE_DEC, HImode,
				       gen_rtx (REG, HImode, HARD_SP_REGNUM)));
       operands[6] = gen_rtx (MEM, HImode,
			      gen_rtx (POST_INC, HImode,
				       gen_rtx (REG, HImode, HARD_SP_REGNUM)));
       operands[5] = operands[2];
       operands[8] = operands[7] = operands[0];
     }
   /* Save the operand2 in a temporary location and use it.  */
   else if ((H_REG_P (operands[2])
             || reg_mentioned_p  (operands[0], operands[2]))
            && !(SP_REG_P (operands[2]) && GET_CODE (operands[3]) == PLUS))
     {
       if (GET_CODE (operands[3]) == MINUS
	   && reg_mentioned_p (operands[0], operands[2]))
	 {
	   operands[9] = gen_rtx (MEM, HImode,
			      gen_rtx (PRE_DEC, HImode,
				       gen_rtx (REG, HImode, HARD_SP_REGNUM)));
	   operands[1] = gen_rtx (MEM, HImode,
			      gen_rtx (POST_INC, HImode,
				       gen_rtx (REG, HImode, HARD_SP_REGNUM)));
	   operands[8] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
	   operands[4] = operands[7] = operands[0];
	   operands[6] = operands[8];
	   operands[5] = operands[2];
	 }
       else 
	 {
       operands[4] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
       operands[6] = operands[4];
       if (!H_REG_P (operands[2]))
	 {
	   operands[5] = operands[0];
	   operands[7] = operands[2];
	   operands[8] = operands[0];
	 }
       else
	 {
           operands[5] = operands[2];
	   operands[8] = operands[7] = operands[0];
	 }
	 }
     }
   else
     {
       operands[4] = operands[5] = operands[0];
       operands[6] = operands[2];
       operands[8] = operands[7] = operands[0];
     }
   ")

(define_split
  [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
	(match_operator:HI 3 "m68hc11_arith_operator"
            [(match_operand:HI 1 "general_operand" "")
	     (match_operand:HI 2 "general_operand" "")]))]
  "z_replacement_completed == 2
   /* If we are adding a small constant to X or Y, it's
     better to use one or several inx/iny instructions.  */
   && !(GET_CODE (operands[3]) == PLUS 
        && ((TARGET_M6812 
	    && (immediate_operand (operands[2], HImode)
		|| hard_reg_operand (operands[2], HImode)))
            || (GET_CODE (operands[2]) == CONST_INT
	        && INTVAL (operands[2]) >= -4
	        && INTVAL (operands[2]) <= 4)))"
  [(set (match_dup 0) (match_dup 1))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:HI D_REGNUM) (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 2)]))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  "
   ")

;;
;; Next split handles the logical operations on D register with
;; another hard register for the second operand.  For this, we
;; have to save the second operand in a scratch location and use
;; it instead.  This must be supported because in some (rare) cases
;; the second operand can come in a hard register and the reload
;; pass doesn't know how to reload it in a memory location.
;;
;;	PLUS MINUS AND IOR XOR
;;
;; The shift operators are special and must not appear here.
;;
(define_split
  [(set (match_operand:HI 0 "d_register_operand" "")
	(match_operator:HI 3 "m68hc11_non_shift_operator"
            [(match_operand:HI 1 "d_register_operand" "")
	     (match_operand:HI 2 "hard_reg_operand" "")]))]
  "TARGET_M6811
   && z_replacement_completed == 2 && !SP_REG_P (operands[2])"
  [(set (match_dup 4) (match_dup 2))
   (set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 4)]))]
  "operands[4] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);")

;;
;; For 68HC12, push the operand[2] value on the stack and do the
;; logical/arithmetic operation with a pop.
;;
(define_split
  [(set (match_operand:HI 0 "d_register_operand" "")
	(match_operator:HI 3 "m68hc11_non_shift_operator"
            [(match_operand:HI 1 "d_register_operand" "")
	     (match_operand:HI 2 "hard_reg_operand" "")]))]
  "TARGET_M6812
   && z_replacement_completed == 2 && !SP_REG_P (operands[2])"
  [(set (match_dup 4) (match_dup 2))
   (set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 5)]))]
  "operands[4] = gen_rtx (MEM, HImode,
			  gen_rtx (PRE_DEC, HImode,
				   gen_rtx (REG, HImode, HARD_SP_REGNUM)));
   operands[5] = gen_rtx (MEM, HImode,
			  gen_rtx (POST_INC, HImode,
				   gen_rtx (REG, HImode, HARD_SP_REGNUM)));
   ")

;;--------------------------------------------------------------------
;; 16-bit Unary operations on X and Y:
;;
;;		NOT NEG
;;
;; Operations on X or Y registers are split here.  Instructions are
;; changed into:
;;   - xgdx/xgdy instruction pattern,
;;   - The same operation on register D,
;;   - xgdx/xgdy instruction pattern.
;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
;; We also handle the case were the address register is used in both source
;; operands, such as:
;;
;;     (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
;; or
;;     (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
;;
(define_split
  [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
	(match_operator:HI 2 "m68hc11_unary_operator"
            [(match_operand 1 "general_operand" "")]))]
  "z_replacement_completed == 2"
  [(set (match_dup 4) (match_dup 5))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:HI D_REGNUM) (match_op_dup 2 [(match_dup 3)]))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  "
{
  if ((H_REG_P (operands[1])
       && !rtx_equal_p (operands[0], operands[1]))
      || reg_mentioned_p (operands[0], operands[1]))
    {
      /* Move to the destination register, before the xgdx.  */
      operands[4] = gen_rtx (REG, GET_MODE (operands[1]), 
			     REGNO (operands[0]));
      operands[5] = operands[1];

      /* Apply the operation on D.  */
      operands[3] = gen_rtx (REG, GET_MODE (operands[1]), HARD_D_REGNUM);
    }
  else
    {
      /* Generate a copy to same register (nop).  */
      operands[4] = operands[5] = operands[0];
      operands[3] = operands[1];
    }
}")

;;
;; 8-bit operations on address registers.
;;
;; We have to take care that the address register is not used for the
;; source of operand2. If operand2 is the D register, we have to save
;; that register in a temporary location.
;;
;; AND OR XOR PLUS MINUS ASHIFT ASHIFTRT LSHIFTRT ROTATE ROTATERT
;;
(define_split
  [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
	(match_operator:QI 3 "m68hc11_arith_operator"
            [(match_operand:QI 1 "hard_addr_reg_operand" "")
	     (match_operand:QI 2 "general_operand" "")]))]
  "z_replacement_completed == 2
   /* Reject a (plus:QI (reg:QI X) (const_int 1|-1)) because the
      incqi pattern generates a better code.  */
   && !(GET_CODE (operands[3]) == PLUS
        && GET_CODE (operands[2]) == CONST_INT
        && (INTVAL (operands[2]) == 1 || INTVAL (operands[2]) == -1))"
  [(set (match_dup 5) (match_dup 6))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 4))
              (set (match_dup 4) (reg:HI D_REGNUM))])
   (set (reg:QI D_REGNUM) (match_op_dup 3 [(reg:QI D_REGNUM) (match_dup 7)]))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 4))
              (set (match_dup 4) (reg:HI D_REGNUM))])]
  "operands[4] = gen_rtx (REG, HImode, REGNO (operands[0]));

   /* For the second operand is a hard register or if the address
      register appears in the source, we have to save the operand[2]
      value in a temporary location and then use that temp.
      Otherwise, it's ok and we generate a (set (D) (D)) that
      will result in a nop.  */
   if (H_REG_P (operands[2]))
     {
       operands[5] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
       operands[6] = gen_rtx (REG, HImode, REGNO (operands[2]));
       operands[7] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);
     }
   else if (reg_mentioned_p (operands[0], operands[2]))
     {
       operands[5] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);
       operands[6] = operands[2];
       operands[7] = operands[5];
     }
   else
     {
       operands[5] = operands[6] = gen_rtx (REG, QImode, HARD_D_REGNUM);
       operands[7] = operands[2];
     }
  ")

;;
;; Next split handles the logical operations on D register with
;; another hard register for the second operand.  For this, we
;; have to save the second operand in a scratch location and use
;; it instead.  This must be supported because in some (rare) cases
;; the second operand can come in a hard register and the reload
;; pass doesn't know how to reload it in a memory location.
;;
;;	PLUS MINUS AND IOR XOR
;;
;; The shift operators are special and must not appear here.
;;
(define_split
  [(set (match_operand:QI 0 "d_register_operand" "")
	(match_operator:QI 3 "m68hc11_non_shift_operator"
            [(match_operand:QI 1 "d_register_operand" "")
	     (match_operand:QI 2 "hard_reg_operand" "")]))]
  "reload_completed"
  [(set (match_dup 5) (match_dup 6))
   (set (match_dup 0) (match_op_dup 3 [(match_dup 0) (match_dup 4)]))]
  "operands[4] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);
   operands[5] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
   operands[6] = gen_rtx (REG, HImode, REGNO (operands[2]));")

;;--------------------------------------------------------------------
;; 8-bit Unary operations on X and Y:
;;
;;		NOT NEG
;;
;; Operations on X or Y registers are split here.  Instructions are
;; changed into:
;;   - xgdx/xgdy instruction pattern,
;;   - The same operation on register D,
;;   - xgdx/xgdy instruction pattern.
;; This should allow the peephole to merge consecutive xgdx/xgdy instructions.
;; We also handle the case were the address register is used in both source
;; operands, such as:
;;
;;     (set (REG:HI X) (PLUS:HI (REG:HI X) (mem:HI (REG:HI X))))
;; or
;;     (set (REG:HI X) (PLUS:HI (REG:HI X) (REG:HI X)))
;;
(define_split
  [(set (match_operand:QI 0 "hard_addr_reg_operand" "")
	(match_operator:QI 2 "m68hc11_unary_operator"
            [(match_operand:QI 1 "general_operand" "")]))]
  "z_replacement_completed == 2"
  [(set (match_dup 4) (match_dup 5))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
              (set (match_dup 3) (reg:HI D_REGNUM))])
   (set (reg:QI D_REGNUM) (match_op_dup 2 [(match_dup 6)]))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 3))
              (set (match_dup 3) (reg:HI D_REGNUM))])]
  "
{
  operands[3] = gen_rtx (REG, HImode, REGNO (operands[0]));
  if ((H_REG_P (operands[1])
       && !rtx_equal_p (operands[0], operands[1]))
      || reg_mentioned_p (operands[0], operands[1]))
    {
      /* Move to the destination register, before the xgdx.  */
      operands[4] = operands[0];
      operands[5] = operands[1];

      /* Apply the operation on D.  */
      operands[6] = gen_rtx (REG, QImode, HARD_D_REGNUM);
    }
  else
    {
      operands[4] = operands[5] = operands[0];
      operands[6] = operands[1];
    }
}")


;;--------------------------------------------------------------------
;;-  Complements
;;--------------------------------------------------------------------

(define_expand "negdi2"
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(neg:DI (match_operand:DI 1 "general_operand" "")))]
  ""
  "m68hc11_emit_libcall (\"__negdi2\", NEG, DImode, DImode, 2, operands);
   DONE;")


(define_insn "negsi2"
  [(set (match_operand:SI 0 "register_operand" "=D")
	(neg:SI (match_operand:SI 1 "general_operand" "0")))]
  ""
  "*
{
  rtx ops[1];

  CC_STATUS_INIT;

  /* With -Os or without -O, use a special library call.  */
  if (optimize_size || optimize == 0)
    return \"bsr\\t___negsi2\";

  ops[0] = gen_label_rtx ();

  /* 32-bit complement and add 1.  */
  output_asm_insn (\"comb\\n\\tcoma\\n\\txgdx\", operands);
  output_asm_insn (\"comb\\n\\tcoma\\n\\tinx\\n\\txgdx\", operands);
  output_asm_insn (\"bne\\t%l0\", ops);
  output_asm_insn (\"inx\", operands);
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\", CODE_LABEL_NUMBER (ops[0]));
  return \"\";
}")

(define_insn "neghi2"
  [(set (match_operand:HI 0 "register_operand" "=d,d,x*y")
	(neg:HI (match_operand:HI 1 "general_operand" "0,!duim,0")))]
  ""
  "@
   coma\\n\\tcomb\\n\\taddd\\t#1
   clra\\n\\tclrb\\n\\tsubd\\t%1
   xgd%0\\n\\tcoma\\n\\tcomb\\n\\txgd%0\\n\\tin%0")

(define_insn "negqi2"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*A")
	(neg:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0")))]
  ""
  "@
   negb
   neg\\t%b0
   neg\\t%b0
   #")

;;
;; - 32-bit complement.  GCC knows how to translate them but providing a
;; pattern generates better/smaller code.
;;
(define_expand "one_cmpldi2"
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(not:DI (match_operand:DI 1 "general_operand" "")))]
  ""
  "m68hc11_emit_libcall (\"___notdi2\", NOT, DImode, DImode, 2, operands);
   DONE;")

(define_insn "one_cmplsi2"
  [(set (match_operand:SI 0 "non_push_operand" "=D,m,!u")
	(not:SI (match_operand:SI 1 "general_operand" "0,m,0")))
   (clobber (match_scratch:HI 2 "=X,d,X"))]
  ""
  "@
   bsr\\t___one_cmplsi2
   #
   #")

(define_insn "one_cmplhi2"
  [(set (match_operand:HI 0 "non_push_operand" "=d,m,*A,u")
	(not:HI (match_operand:HI 1 "general_operand" "0,0,0,0")))]
  ""
  "@
   comb\\n\\tcoma
   com\\t%b0\\n\\tcom\\t%h0
   #
   com\\t%b0\\n\\tcom\\t%h0")

(define_insn "one_cmplqi2"
  [(set (match_operand:QI 0 "non_push_operand" "=d,m,*A,u")
	(not:QI (match_operand:QI 1 "general_operand" "0,0,0,0")))]
  ""
  "@
   comb
   com\\t%b0
   #
   com\\t%b0")

(define_split /* "*one_cmplsi2" */
  [(set (match_operand:SI 0 "non_push_operand" "")
	(not:SI (match_dup 0)))
   (clobber (match_scratch:HI 1 ""))]
  "z_replacement_completed == 2
   && (!X_REG_P (operands[0]) || (optimize && optimize_size == 0))"
  [(set (match_dup 2) (not:HI (match_dup 2)))
   (set (match_dup 3) (not:HI (match_dup 3)))]
  "operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
   operands[3] = m68hc11_gen_highpart (HImode, operands[0]);")

(define_split /* "*one_cmplsi2" */
  [(set (match_operand:SI 0 "non_push_operand" "")
	(not:SI (match_operand:SI 1 "non_push_operand" "")))
   (clobber (match_operand:HI 2 "d_register_operand" ""))]
  "z_replacement_completed == 2
   && (!X_REG_P (operands[0]) || (optimize && optimize_size == 0))"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 2) (not:HI (match_dup 2)))
   (set (match_dup 4) (match_dup 2))
   (set (match_dup 2) (match_dup 5))
   (set (match_dup 2) (not:HI (match_dup 2)))
   (set (match_dup 6) (match_dup 2))]
  "operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);
   operands[5] = m68hc11_gen_highpart (HImode, operands[1]);
   operands[4] = m68hc11_gen_lowpart (HImode, operands[0]);
   operands[6] = m68hc11_gen_highpart (HImode, operands[0]);")

;;--------------------------------------------------------------------
;;- arithmetic shifts
;;--------------------------------------------------------------------
;;
;; Provide some 64-bit shift patterns. 
(define_expand "ashldi3"
  [(parallel [(set (match_operand:DI 0 "nonimmediate_operand" "")
	             (ashift:DI (match_operand:DI 1 "general_operand" "")
		                (match_operand:HI 2 "general_operand" "")))
              (clobber (match_scratch:HI 3 ""))])]
   ""
   "
{
  if (GET_CODE (operands[2]) != CONST_INT 
      || (INTVAL (operands[2]) != 32 && INTVAL (operands[2]) != 1))
    {
      FAIL;
    }
}")

(define_insn "*ashldi3_const32"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=<,m,u")
	(ashift:DI (match_operand:DI 1 "general_operand" "umi,umi,umi")
		   (const_int 32)))
   (clobber (match_scratch:HI 2 "=&A,d,d"))]
   ""
   "#")

(define_split
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(ashift:DI (match_operand:DI 1 "general_operand" "")
		   (const_int 32)))
   (clobber (match_scratch:HI 2 ""))]
   "reload_completed"
   [(const_int 0)]
   "/* Move the lowpart in the highpart first in case the shift
       is applied on the source.  */
    if (IS_STACK_PUSH (operands[0]))
      {
         m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
			     const0_rtx, operands[2]);
      }
    m68hc11_split_move (m68hc11_gen_highpart (SImode, operands[0]),
		        m68hc11_gen_lowpart (SImode, operands[1]),
		        operands[2]);
    if (!IS_STACK_PUSH (operands[0]))
      {
        m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
			    const0_rtx, operands[2]);
      }
    DONE;")

(define_insn "*ashldi3_const1"
  [(set (match_operand:DI 0 "non_push_operand" "=m,m,u")
	(ashift:DI (match_operand:DI 1 "general_operand" "mi,u,umi")
		   (const_int 1)))
   (clobber (match_scratch:HI 2 "=d,d,d"))]
   ""
   "#")

(define_split
  [(set (match_operand:DI 0 "non_push_operand" "")
	(ashift:DI (match_operand:DI 1 "general_operand" "")
		   (const_int 1)))
   (clobber (match_scratch:HI 2 ""))]
   "z_replacement_completed == 2"
   [(set (match_dup 2) (match_dup 3))
    (set (match_dup 2) (ashift:HI (match_dup 2) (const_int 1)))
    (set (match_dup 4) (match_dup 2))

    (set (match_dup 2) (match_dup 5))
    (parallel [(set (match_dup 2)
		       (rotate:HI (match_dup 2) (const_int 1)))
	       (clobber (reg:HI CC_REGNUM))])
    (set (match_dup 6) (match_dup 2))

    (set (match_dup 2) (match_dup 7))
    (parallel [(set (match_dup 2)
		       (rotate:HI (match_dup 2) (const_int 1)))
	       (clobber (reg:HI CC_REGNUM))])
    (set (match_dup 8) (match_dup 2))

    (set (match_dup 2) (match_dup 9))
    (parallel [(set (match_dup 2)
		       (rotate:HI (match_dup 2) (const_int 1)))
	       (clobber (reg:HI CC_REGNUM))])
    (set (match_dup 10) (match_dup 2))]
   "operands[3] = m68hc11_gen_lowpart (SImode, operands[1]);
    operands[5] = m68hc11_gen_highpart (HImode, operands[3]);
    operands[3] = m68hc11_gen_lowpart (HImode, operands[3]);

    operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
    operands[6] = m68hc11_gen_highpart (HImode, operands[4]);
    operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);

    operands[7] = m68hc11_gen_highpart (SImode, operands[1]);
    operands[9] = m68hc11_gen_highpart (HImode, operands[7]);
    operands[7] = m68hc11_gen_lowpart (HImode, operands[7]);

    operands[8] = m68hc11_gen_highpart (SImode, operands[0]);
    operands[10] = m68hc11_gen_highpart (HImode, operands[8]);
    operands[8] = m68hc11_gen_lowpart (HImode, operands[8]);")

(define_insn "addsi_silshr16"
  [(set (match_operand:SI 0 "register_operand" "=D,D")
          (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "!*uim,0")
				(const_int 16))
		   (match_operand:SI 2 "general_operand" "0,m!*u")))]
  ""
  "#")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
          (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "")
				(const_int 16))
		   (match_operand:SI 2 "general_operand" "")))]
  "z_replacement_completed == 2 && !X_REG_P (operands[1])"
  [(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
   (set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM)
					    (const_int 0))
				   (reg:HI CC_REGNUM)))]
  "operands[3] = m68hc11_gen_highpart (HImode, operands[1]);")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
          (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "")
				(const_int 16))
		   (match_operand:SI 2 "general_operand" "")))]
  "z_replacement_completed == 2 && X_REG_P (operands[1])"
  [(set (reg:HI D_REGNUM) (reg:HI X_REGNUM))
   (set (reg:HI X_REGNUM) (match_dup 3))
   (set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 4)))
   (set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM)
					    (const_int 0))
				   (reg:HI CC_REGNUM)))]
  "operands[3] = m68hc11_gen_highpart (HImode, operands[2]);
   operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);")

(define_insn "addsi_ashift16"
  [(set (match_operand:SI 0 "register_operand" "=D")
          (plus:SI 
		   (mult:SI (match_operand:SI 2 "general_operand" "uim")
			    (const_int 65536))
		(match_operand:SI 1 "general_operand" "0")))
   (clobber (match_scratch:HI 3 "=X"))]
  "0"
  "#")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
          (plus:SI 
		   (mult:SI (match_operand:SI 2 "general_operand" "")
			    (const_int 65536))
		   (match_operand:SI 1 "general_operand" "")))
   (clobber (match_scratch:HI 3 "=X"))]
  "0 && reload_completed && z_replacement_completed == 2"
  [(set (reg:HI X_REGNUM) (plus:HI (reg:HI X_REGNUM) (match_dup 4)))]
  "
{
  operands[4] = m68hc11_gen_lowpart (HImode, operands[2]);
}")

(define_insn "addsi_andshr16"
  [(set (match_operand:SI 0 "register_operand" "=D")
          (plus:SI (and:SI (match_operand:SI 1 "general_operand" "%uim")
			   (const_int 65535))
		   (match_operand:SI 2 "general_operand" "0")))]
  ""
  "#")

(define_split
  [(set (match_operand:SI 0 "register_operand" "")
          (plus:SI (and:SI (match_operand:SI 1 "general_operand" "")
			   (const_int 65535))
		   (match_operand:SI 2 "general_operand" "")))]
  "z_replacement_completed == 2"
  [(set (reg:HI D_REGNUM) (plus:HI (reg:HI D_REGNUM) (match_dup 3)))
   (set (reg:HI X_REGNUM) (plus:HI (plus:HI (reg:HI X_REGNUM) (const_int 0)) (reg:HI CC_REGNUM)))]
  "operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);")

;;
;; 32-bit shifts are made by a small library routine that uses
;; a specific passing convention for parameters (for efficiency reasons).
;;
;; [D + X] -> Value to be shifted
;; Y       -> Shift count
;;
;; The shift count is clobbered by the routine.
;;
(define_expand "ashlsi3"
  [(parallel
       [(set (match_operand:SI 0 "register_operand" "") 
	     (match_operand:SI 1 "general_operand" ""))
	(clobber (scratch:HI))])
   (parallel
     [(set (match_dup 0) (ashift:SI (match_dup 0)
			 (match_operand:HI 2 "nonmemory_operand" "")))
      (clobber (scratch:HI))])]
   ""
   "")

(define_split
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(ashift:SI (match_operand:SI 1 "general_operand" "")
	           (const_int 16)))
   (clobber (match_scratch:HI 3 ""))]
   ""
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (const_int 0))]
   "operands[2] = m68hc11_gen_highpart (HImode, operands[0]);
    operands[4] = m68hc11_gen_lowpart (HImode, operands[0]);
    operands[3] = m68hc11_gen_lowpart (HImode, operands[1]);")

(define_insn "*ashlsi3_const16"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=D,m,*u")
	(ashift:SI (match_operand:SI 1 "general_operand" "Duim,D,D")
	           (const_int 16)))
   (clobber (match_scratch:HI 2 "=X,X,X"))]
   ""
   "#")

(define_insn "*ashlsi3_const16_zexthi"
  [(set (match_operand:SI 0 "nonimmediate_operand" "=D")
	(ashift:SI (zero_extend:HI 
			(match_operand:HI 1 "general_operand" "duim*A"))
	           (const_int 16)))
   (clobber (match_scratch:HI 2 "=X"))]
   ""
   "#")

(define_split /* "*ashlsi3_const16_zexthi"*/
  [(set (match_operand:SI 0 "nonimmediate_operand" "")
	(ashift:SI (zero_extend:HI 
			(match_operand:HI 1 "general_operand" ""))
	           (const_int 16)))
   (clobber (match_scratch:HI 2 "=X"))]
   "reload_completed"
   [(set (reg:HI X_REGNUM) (match_dup 1))
    (set (reg:HI D_REGNUM) (const_int 0))]
   "")

(define_insn "*ashlsi3_const1"
  [(set (match_operand:SI 0 "non_push_operand" "=D,D,m,!*u,?*um")
	(ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0,*um,0,0,*um")
	           (const_int 1)))
   (clobber (match_scratch:HI 2 "=X,X,&d,&d,&d"))]
   ""
   "*
{
  CC_STATUS_INIT;
  if (X_REG_P (operands[1]))
    {
      return \"lsld\\n\\txgdx\\n\\trolb\\n\\trola\\n\\txgdx\";
    }
  else
    {
      rtx ops[2];

      ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
      ops[0] = gen_rtx (REG, HImode, HARD_D_REGNUM);
      m68hc11_gen_movhi (insn, ops);
      output_asm_insn (\"lsld\", ops);
      if (!X_REG_P (operands[0]))
	{
	  ops[1] = ops[0];
	  ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
	  m68hc11_gen_movhi (insn, ops);
	  ops[0] = ops[1];
          ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
          m68hc11_gen_movhi (insn, ops);
	}
      else
	{
	  /* Load the high part in X in case the source operand
	     uses X as a memory pointer.  */
	  ops[0] = gen_rtx (REG, HImode, HARD_X_REGNUM);
          ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
          m68hc11_gen_movhi (insn, ops);
          output_asm_insn (\"xgdx\", ops);
	}
      output_asm_insn (\"rolb\", ops);
      output_asm_insn (\"rola\", ops);
      if (!X_REG_P (operands[0]))
	{
	  ops[1] = ops[0];
	  ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
	  m68hc11_gen_movhi (insn, ops);
	}
      else
        {
          output_asm_insn (\"xgdx\", ops);
        }
      return \"\";
    }
}")

(define_insn "*ashlsi3_const"
  [(set (match_operand:SI 0 "register_operand" "+D")
	(ashift:SI (match_dup 0)
	           (match_operand:HI 1 "const_int_operand" "")))
   (clobber (match_scratch:HI 2 "=y"))]
   ""
   "*
{
  CC_STATUS_INIT;
  return \"ldy\\t%1\\n\\tbsr\\t___ashlsi3\";
}")

(define_insn "*ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "+D,D")
	(ashift:SI (match_dup 0)
	           (match_operand:HI 1 "general_operand" "y,m")))
   (clobber (match_scratch:HI 2 "=1,X"))]
   ""
   "*
{
  CC_STATUS_INIT;

  /* There is a reload problem if we don't accept 'm' for the shift value.
     A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
     and this conflicts with all reloads.  Since X, Y, Z are used there
     is not enough register in class A_REGS.

     Assuming that 'operands[1]' does not refer to the stack (which 
     is true for 68hc11 only, we save temporary the value of Y.  */
  if (!Y_REG_P (operands[2]))
    {
      rtx ops[1];
      int y_dead = dead_register_here (insn, iy_reg);

      ops[0] = operands[1];
      if (y_dead == 0)
	{
          output_asm_insn (\"pshy\", operands);
          if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
	    ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
	}
      output_asm_insn (\"ldy\\t%0\", ops);
      output_asm_insn (\"bsr\\t___ashlsi3\", operands);
      return y_dead == 0 ? \"puly\" : \"\";
    }
  return \"bsr\\t___ashlsi3\";
}")

(define_expand "ashlhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(ashift:HI (match_operand:HI 1 "register_operand" "")
	           (match_operand:HI 2 "general_operand" "")))]
   ""
   "
{
  if (GET_CODE (operands[2]) != CONST_INT) 
    {
      rtx scratch = gen_reg_rtx (HImode);
      emit_move_insn (scratch, operands[2]);
      emit_insn (gen_rtx (PARALLEL, VOIDmode,
	 	 gen_rtvec (2, gen_rtx (SET, VOIDmode,
			    operand0,
			    gen_rtx_ASHIFT (HImode,
					operand1, scratch)),
			      gen_rtx (CLOBBER, VOIDmode, scratch))));
      DONE;
    }
}")

(define_insn "*ashlhi3_const1"
  [(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
	(ashift:HI (match_operand:HI 1 "non_push_operand" "0,0")
	           (const_int 1)))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  if (D_REG_P (operands[0]))
    {
      return \"asld\";
    }
  
  output_asm_insn (\"asl\\t%b0\", operands);
  output_asm_insn (\"rol\\t%h0\", operands);
  CC_STATUS_INIT;
  return \"\";
}")


(define_insn "*ashlhi3_2"
  [(set (match_operand:HI 0 "register_operand" "=d,*x")
	(ashift:HI (match_operand:HI 1 "register_operand" "0,0")
                   (match_operand:HI 2 "register_operand" "+x,+d")))
   (clobber (match_dup 2))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  CC_STATUS_INIT;
  return \"bsr\\t___lshlhi3\";
}")

(define_insn "*ashlhi3"
  [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d"))
	(ashift:HI (match_dup 0)
		   (match_operand:HI 1 "register_operand" "+x")))
   (clobber (match_dup 1))]
  ""
  "*
{
  CC_STATUS_INIT;
  return \"bsr\\t___lshlhi3\";
}")

(define_insn "*ashlhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,!*A")
	(ashift:HI (match_operand:HI 1 "register_operand" "0,0")
	           (match_operand:HI 2 "const_int_operand" "")))]
  ""
  "*
{
  int	i;

  if (A_REG_P (operands[0]))
    return \"#\";

  i = INTVAL (operands[2]);
  if (i >= 8)
    {
      CC_STATUS_INIT;
      output_asm_insn (\"tba\", operands);
      if (i == 15)
        {
	  output_asm_insn (\"rora\", operands);
	  output_asm_insn (\"anda\\t#0\", operands);
	  output_asm_insn (\"rora\", operands);
	}
      else
        while (i != 8 )
          {
            output_asm_insn (\"asla\", operands);
	    i--;
	  }
      return \"clrb\";
    }
  for (i = 0; i < INTVAL (operands[2]) - 1; i++) 
    {
      output_asm_insn (\"asld\", operands);
    }
  return \"asld\";
}")

(define_expand "ashlqi3"
  [(set (match_operand:QI 0 "register_operand" "")
	(ashift:QI (match_operand:QI 1 "register_operand" "")
	           (match_operand:QI 2 "general_operand" "")))]
   ""
   "")

(define_insn "*ashlqi3_const1"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*q,!*A")
	(ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
	           (const_int 1)))]
  ""
  "@
   aslb
   asl\\t%b0
   asl\\t%b0
   asl%0
   #")

(define_insn "*ashlqi3_const"
  [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
	(ashift:QI (match_operand:QI 1 "register_operand" "0,0,0")
	           (match_operand:QI 2 "const_int_operand" "")))]
  ""
  "*
{
  int i;
  const char* insn_code;

  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    insn_code = \"aslb\";
  else if (DA_REG_P (operands[0]))
    insn_code = \"asla\";
  else
    return \"#\";

  i = INTVAL (operands[2]);
  if (i >= 8)
    {
      if (DA_REG_P (operands[0]))
        return \"clra\";
      else
        return \"clrb\";
    }
  else if (i == 7)
    {
      if (DA_REG_P (operands[0]))
        {
          output_asm_insn (\"rora\", operands);
          output_asm_insn (\"ldaa\\t#0\", operands);
          return \"rora\";
        }
      else
        {
          output_asm_insn (\"rorb\", operands);
          output_asm_insn (\"ldab\\t#0\", operands);
          return \"rorb\";
        }
    }
  else if (i == 6)
    {
      if (DA_REG_P (operands[0]))
        {
          output_asm_insn (\"rora\", operands);
          output_asm_insn (\"rora\", operands);
          output_asm_insn (\"rora\", operands);
          return \"anda\\t#0xC0\";
        }
      else
        {
          output_asm_insn (\"rorb\", operands);
          output_asm_insn (\"rorb\", operands);
          output_asm_insn (\"rorb\", operands);
          return \"andb\\t#0xC0\";
        }
    }
  while (--i >= 0)
    {
      output_asm_insn (insn_code, operands);
    }
  return \"\";
}")

(define_insn "*ashlqi3"
  [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
	(ashift:QI (match_operand:QI 1 "register_operand" "0,0,0")
	             (match_operand:QI 2 "nonimmediate_operand" 
					 "m*u*d*A,m*u*d*A,m*u")))]
  ""
  "*
{
  rtx ops[2];

  if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
    return \"#\";

  ops[0] = gen_rtx (REG, QImode, HARD_A_REGNUM);
  ops[1] = operands[2];
  m68hc11_gen_movqi (insn, ops);

  CC_STATUS_INIT;
  return \"bsr\\t___lshlqi3\";
}")

(define_expand "ashrhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(ashiftrt:HI (match_operand:HI 1 "register_operand" "")
	             (match_operand:HI 2 "general_operand" "")))]
   ""
   "
{
  if (GET_CODE (operands[2]) != CONST_INT) 
    {
      rtx scratch = gen_reg_rtx (HImode);

      emit_move_insn (scratch, operands[2]);
      emit_insn (gen_rtx (PARALLEL, VOIDmode,
		 gen_rtvec (2, gen_rtx (SET, VOIDmode,
				operand0,
				gen_rtx_ASHIFTRT (HImode,
					operand1, scratch)),
			      gen_rtx (CLOBBER, VOIDmode, scratch))));
       DONE;
    }
}")

(define_insn "*ashrhi3_const1"
  [(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
	(ashiftrt:HI (match_operand:HI 1 "non_push_operand" "0,0")
	             (const_int 1)))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  CC_STATUS_INIT;
  if (D_REG_P (operands[0]))
    {
      return \"asra\\n\\trorb\";
    }
  
  output_asm_insn (\"asr\\t%h0\", operands);
  output_asm_insn (\"ror\\t%b0\", operands);
  return \"\";
}")


(define_insn "*ashrhi3_const"
  [(set (match_operand:HI 0 "register_operand" "=d,!*A")
	(ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
	             (match_operand:HI 2 "const_int_operand" "")))]
  ""
  "*
{
  rtx ops[2];
  int val = INTVAL (operands[2]);

  if (A_REG_P (operands[0]))
    return \"#\";

  if (val >= 15)
    {
      ops[0] = gen_label_rtx ();

      output_asm_insn (\"clrb\", operands);
      output_asm_insn (\"rola\", operands);

	/* Clear A without clearing the carry flag.  */
      output_asm_insn (\"tba\", operands);
      output_asm_insn (\"bcc\\t%l0\", ops);
      output_asm_insn (\"coma\", operands);
      output_asm_insn (\"comb\", operands);

      CC_STATUS_INIT;
      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[0]));
      return \"\";
    }
  if (val >= 8)
    {
      ops[0] = gen_label_rtx ();

      output_asm_insn (\"tab\", operands);
      output_asm_insn (\"clra\", operands);
      output_asm_insn (\"tstb\", operands);
      output_asm_insn (\"bge\\t%l0\", ops);
      output_asm_insn (\"deca\", operands);

      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[0]));

      val -= 8;

      while (val > 0)
        {
	  output_asm_insn (\"asrb\", operands);
	  val--;
        }
	/* Status is ok.  */
      return \"\";
    }
  if (val == 7)
    {
      ops[0] = gen_label_rtx ();
      output_asm_insn (\"rolb\", operands);
      output_asm_insn (\"rola\", operands);
      output_asm_insn (\"tab\", operands);
      output_asm_insn (\"anda\\t#0\", operands);
      output_asm_insn (\"bcc\\t%l0\", ops);
      output_asm_insn (\"coma\", ops);

      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
				 CODE_LABEL_NUMBER (ops[0]));
      return \"\";
    }
  while (val > 0)
    {
      output_asm_insn (\"asra\", operands);
      output_asm_insn (\"rorb\", operands);
      val--;
    }
  CC_STATUS_INIT;

  return \"\";
}")

(define_insn "*ashrhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,*x")
	(ashiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
	             (match_operand:HI 2 "register_operand" "+x,+d")))
   (clobber (match_dup 2))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  output_asm_insn (\"bsr\\t___ashrhi3\", operands);
  return \"\"; 
}")

(define_expand "ashrsi3"
  [(parallel
       [(set (match_dup 0) (match_operand:SI 1 "general_operand" ""))
	(clobber (scratch:HI))])
   (parallel
       [(set (match_operand:SI 0 "register_operand" "")
		(ashiftrt:SI (match_dup 0)
		             (match_operand:HI 2 "general_operand" "")))
        (clobber (scratch:HI))])]
   ""
   "")

(define_insn "*ashrsi3_const"
  [(set (match_operand:SI 0 "register_operand" "+D")
	(ashiftrt:SI (match_dup 0)
	             (match_operand:HI 1 "const_int_operand" "")))
   (clobber (match_scratch:HI 2 "=y"))]
   ""
   "*
{
  CC_STATUS_INIT;
  return \"ldy\\t%1\\n\\tbsr\\t___ashrsi3\";
}")

(define_insn "*ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "+D,D")
	(ashiftrt:SI (match_dup 0)
	             (match_operand:HI 1 "general_operand" "y,m")))
   (clobber (match_scratch:HI 2 "=1,X"))]
   ""
   "*
{
  CC_STATUS_INIT;
  /* There is a reload problem if we don't accept 'm' for the shift value.
     A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
     and this conflicts with all reloads.  Since X, Y, Z are used there
     is not enough register in class A_REGS.

     Assuming that 'operands[1]' does not refer to the stack (which 
     is true for 68hc11 only, we save temporary the value of Y.  */
  if (!Y_REG_P (operands[2]))
    {
      rtx ops[1];
      int y_dead = dead_register_here (insn, iy_reg);

      ops[0] = operands[1];
      if (y_dead == 0)
	{
          output_asm_insn (\"pshy\", operands);
          if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
	    ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
	}
      output_asm_insn (\"ldy\\t%0\", ops);
      output_asm_insn (\"bsr\\t___ashrsi3\", operands);
      return y_dead == 0 ? \"puly\" : \"\";
    }
  return \"bsr\\t___ashrsi3\";
}")

(define_expand "ashrqi3"
  [(set (match_operand:QI 0 "register_operand" "")
	(ashiftrt:QI (match_operand:QI 1 "register_operand" "")
	             (match_operand:QI 2 "general_operand" "")))]
   ""
   "")

(define_insn "*ashrqi3_const1"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,m,!u,!*q,!*A")
	(ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
		     (const_int 1)))]
  ""
  "@
   asrb
   asr\\t%b0
   asr\\t%b0
   asr%0
   #")

(define_insn "*ashrqi3_const"
  [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
	(ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
	             (match_operand:QI 2 "const_int_operand" "")))]
  ""
  "*
{
  int i;
  const char* insn_code;

  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    insn_code = \"asrb\";
  else if (DA_REG_P (operands[0]))
    insn_code = \"asra\";
  else
    return \"#\";

  i = INTVAL (operands[2]);
  if (i > 8)
    i = 8;
  while (--i >= 0)
    {
      output_asm_insn (insn_code, operands);
    }
  return \"\";
}")

(define_insn "*ashrqi3"
  [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
	(ashiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
	             (match_operand:QI 2 "nonimmediate_operand" 
					 "m*u*d*A,m*u*d*A,m*u")))]
  ""
  "*
{
  rtx ops[2];

  if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
    return \"#\";

  ops[0] = gen_rtx (REG, QImode, HARD_A_REGNUM);
  ops[1] = operands[2];
  m68hc11_gen_movqi (insn, ops);

  CC_STATUS_INIT;
  return \"bsr\\t___ashrqi3\";
}")

;;--------------------------------------------------------------------
;; logical shift instructions
;;--------------------------------------------------------------------
(define_expand "lshrdi3"
  [(parallel [(set (match_operand:DI 0 "general_operand" "")
	             (lshiftrt:DI (match_operand:DI 1 "general_operand" "")
		                  (match_operand:HI 2 "general_operand" "")))
              (clobber (match_scratch:HI 3 ""))])]
   ""
   "
{
  if (GET_CODE (operands[2]) != CONST_INT 
     || (INTVAL (operands[2]) != 32 && INTVAL (operands[2]) < 48
         && INTVAL (operands[2]) != 1))
    {
      FAIL;
    }
}")

(define_insn "*lshrdi3_const32"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=<,m,u")
	(lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi,umi")
		     (const_int 32)))
   (clobber (match_scratch:HI 2 "=&A,d,d"))]
   ""
   "#")

(define_split
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(lshiftrt:DI (match_operand:DI 1 "general_operand" "")
		     (const_int 32)))
   (clobber (match_scratch:HI 2 "=&A,d"))]
   "reload_completed"
   [(const_int 0)]
   "m68hc11_split_move (m68hc11_gen_lowpart (SImode, operands[0]),
		        m68hc11_gen_highpart (SImode, operands[1]),
		        operands[2]);
    m68hc11_split_move (m68hc11_gen_highpart (SImode, operands[0]),
			const0_rtx, operands[2]);
    DONE;")

(define_insn "*lshrdi3_const63"
  [(set (match_operand:DI 0 "nonimmediate_operand" "=m,u")
	(lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
		     (match_operand:DI 2 "const_int_operand" "")))
   (clobber (match_scratch:HI 3 "=d,d"))]
   "INTVAL (operands[2]) >= 48"
   "#")

(define_split
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(lshiftrt:DI (match_operand:DI 1 "general_operand" "")
		     (match_operand:DI 2 "const_int_operand" "")))
   (clobber (match_scratch:HI 3 "=d"))]
   "z_replacement_completed && INTVAL (operands[2]) >= 56"
   [(set (reg:QI D_REGNUM) (match_dup 9))
    (set (reg:QI D_REGNUM) (lshiftrt:QI (reg:QI D_REGNUM) (match_dup 8)))
    (set (reg:HI D_REGNUM) (zero_extend:HI (reg:QI D_REGNUM)))
    (set (match_dup 4) (reg:HI D_REGNUM))
    (set (reg:QI D_REGNUM) (const_int 0))
    (set (match_dup 5) (reg:HI D_REGNUM))
    (set (match_dup 6) (reg:HI D_REGNUM))
    (set (match_dup 7) (reg:HI D_REGNUM))]
   "operands[8] = GEN_INT (INTVAL (operands[2]) - 56);
    operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
    operands[5] = m68hc11_gen_highpart (HImode, operands[4]);
    operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);

    operands[9] = m68hc11_gen_highpart (SImode, operands[1]);
    operands[9] = m68hc11_gen_highpart (HImode, operands[9]);
    operands[9] = m68hc11_gen_highpart (QImode, operands[9]);

    operands[6] = m68hc11_gen_highpart (SImode, operands[0]);
    operands[7] = m68hc11_gen_highpart (HImode, operands[6]);
    operands[6] = m68hc11_gen_lowpart (HImode, operands[6]);")

(define_split
  [(set (match_operand:DI 0 "nonimmediate_operand" "")
	(lshiftrt:DI (match_operand:DI 1 "general_operand" "")
		     (match_operand:DI 2 "const_int_operand" "")))
   (clobber (match_scratch:HI 3 "=d"))]
   "z_replacement_completed && INTVAL (operands[2]) >= 48 
    && INTVAL (operands[2]) < 56"
   [(set (reg:HI D_REGNUM) (match_dup 9))
    (set (reg:HI D_REGNUM) (lshiftrt:HI (reg:HI D_REGNUM) (match_dup 8)))
    (set (match_dup 4) (reg:HI D_REGNUM))
    (set (reg:HI D_REGNUM) (const_int 0))
    (set (match_dup 5) (reg:HI D_REGNUM))
    (set (match_dup 6) (reg:HI D_REGNUM))
    (set (match_dup 7) (reg:HI D_REGNUM))]
   "operands[8] = GEN_INT (INTVAL (operands[2]) - 48);
    operands[4] = m68hc11_gen_lowpart (SImode, operands[0]);
    operands[5] = m68hc11_gen_highpart (HImode, operands[4]);
    operands[4] = m68hc11_gen_lowpart (HImode, operands[4]);

    operands[9] = m68hc11_gen_highpart (SImode, operands[1]);
    operands[9] = m68hc11_gen_highpart (HImode, operands[1]);
    operands[6] = m68hc11_gen_highpart (SImode, operands[0]);
    operands[7] = m68hc11_gen_highpart (HImode, operands[6]);
    operands[6] = m68hc11_gen_lowpart (HImode, operands[6]);")

(define_insn "*lshrdi_const1"
  [(set (match_operand:DI 0 "non_push_operand" "=m,u")
	(lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
		     (const_int 1)))
   (clobber (match_scratch:HI 2 "=d,d"))]
   ""
   "#")

(define_split
  [(set (match_operand:DI 0 "non_push_operand" "")
	(lshiftrt:DI (match_operand:DI 1 "general_operand" "")
		     (const_int 1)))
   (clobber (match_scratch:HI 2 ""))]
   "z_replacement_completed == 2"
   [(set (match_dup 2) (match_dup 3))
    (set (match_dup 2) (lshiftrt:HI (match_dup 2) (const_int 1)))
    (set (match_dup 4) (match_dup 2))

    (set (match_dup 2) (match_dup 5))
    (parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
               (clobber (reg:HI CC_REGNUM))])
    (set (match_dup 6) (match_dup 2))

    (set (match_dup 2) (match_dup 7))
    (parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
               (clobber (reg:HI CC_REGNUM))])
    (set (match_dup 8) (match_dup 2))

    (set (match_dup 2) (match_dup 9))
    (parallel [(set (match_dup 2) (rotatert:HI (match_dup 2) (const_int 1)))
               (clobber (reg:HI CC_REGNUM))])
    (set (match_dup 10) (match_dup 2))]
   "operands[3] = m68hc11_gen_highpart (SImode, operands[1]);
    operands[5] = m68hc11_gen_lowpart (HImode, operands[3]);
    operands[3] = m68hc11_gen_highpart (HImode, operands[3]);

    operands[4] = m68hc11_gen_highpart (SImode, operands[0]);
    operands[6] = m68hc11_gen_lowpart (HImode, operands[4]);
    operands[4] = m68hc11_gen_highpart (HImode, operands[4]);

    operands[7] = m68hc11_gen_lowpart (SImode, operands[1]);
    operands[9] = m68hc11_gen_lowpart (HImode, operands[7]);
    operands[7] = m68hc11_gen_highpart (HImode, operands[7]);

    operands[8] = m68hc11_gen_lowpart (SImode, operands[0]);
    operands[10] = m68hc11_gen_lowpart (HImode, operands[8]);
    operands[8] = m68hc11_gen_highpart (HImode, operands[8]);")

(define_expand "lshrsi3"
  [(parallel
       [(set (match_dup 0) (match_operand:SI 1 "general_operand" ""))
	(clobber (scratch:HI))])
   (parallel
       [(set (match_operand:SI 0 "register_operand" "")
	     (lshiftrt:SI (match_dup 0)
		          (match_operand:HI 2 "general_operand" "")))
        (clobber (scratch:HI))])]
   ""
   "")

(define_split
  [(set (match_operand:SI 0 "non_push_operand" "")
	(lshiftrt:SI (match_operand:SI 1 "general_operand" "")
	             (const_int 16)))
   (clobber (match_scratch:HI 3 ""))]
   "reload_completed && !(X_REG_P (operands[0]) && X_REG_P (operands[1]))"
  [(set (match_dup 2) (match_dup 3))
   (set (match_dup 4) (const_int 0))]
   "operands[4] = m68hc11_gen_highpart (HImode, operands[0]);
    operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
    operands[3] = m68hc11_gen_highpart (HImode, operands[1]);")

(define_insn "*lshrsi3_const16"
  [(set (match_operand:SI 0 "non_push_operand" "=D,D,m,u")
	(lshiftrt:SI (match_operand:SI 1 "general_operand" "uim,0,D,D")
	             (const_int 16)))
   (clobber (match_scratch:HI 2 "=X,X,X,X"))]
   ""
   "@
    #
    xgdx\\n\\tldx\\t#0
    #
    #")

(define_insn "*lshrsi3_const1"
  [(set (match_operand:SI 0 "non_push_operand" "=D,m,*u")
	(lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "D*um,*um,*um")
	             (const_int 1)))
   (clobber (match_scratch:HI 2 "=X,&d,&d"))]
   ""
   "*
{
  CC_STATUS_INIT;
  if (X_REG_P (operands[1]))
    {
      return \"xgdx\\n\\tlsrd\\n\\txgdx\\n\\trora\\n\\trorb\";
    }
  else
    {
      rtx ops[2];

      ops[1] = m68hc11_gen_highpart (HImode, operands[1]);
      ops[0] = gen_rtx (REG, HImode, HARD_D_REGNUM);
      m68hc11_gen_movhi (insn, ops);
      output_asm_insn (\"lsrd\", ops);
      if (!X_REG_P (operands[0]))
	{
	  ops[1] = ops[0];
	  ops[0] = m68hc11_gen_highpart (HImode, operands[0]);
	  m68hc11_gen_movhi (insn, ops);
	  ops[0] = ops[1];
          ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
          m68hc11_gen_movhi (insn, ops);
	}
      else
	{
	  /* Load the lowpart in X in case the operands is some N,x.  */
	  ops[0] = gen_rtx (REG, HImode, HARD_X_REGNUM);
          ops[1] = m68hc11_gen_lowpart (HImode, operands[1]);
          m68hc11_gen_movhi (insn, ops);
          output_asm_insn (\"xgdx\", ops);
	}
      output_asm_insn (\"rora\", ops);
      output_asm_insn (\"rorb\", ops);
      if (!X_REG_P (operands[0]))
	{
	  ops[1] = ops[0];
	  ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
	  m68hc11_gen_movhi (insn, ops);
	}
      return \"\";
    }
}")

(define_insn "*lshrsi3_const"
  [(set (match_operand:SI 0 "register_operand" "+D")
	(lshiftrt:SI (match_dup 0)
	             (match_operand:HI 1 "const_int_operand" "")))
   (clobber (match_scratch:HI 2 "=y"))]
   ""
   "*
{
  CC_STATUS_INIT;
  return \"ldy\\t%1\\n\\tbsr\\t___lshrsi3\";
}")

(define_insn "*lshrsi3"
  [(set (match_operand:SI 0 "register_operand" "+D,D")
	(lshiftrt:SI (match_dup 0)
	             (match_operand:HI 1 "general_operand" "y,m")))
   (clobber (match_scratch:HI 2 "=1,X"))]
   ""
   "*
{
  CC_STATUS_INIT;
  /* There is a reload problem if we don't accept 'm' for the shift value.
     A RELOAD_OTHER reload can be generated for operand 0 (class A_REGS)
     and this conflicts with all reloads.  Since X, Y, Z are used there
     is not enough register in class A_REGS.

     Assuming that 'operands[1]' does not refer to the stack (which 
     is true for 68hc11 only, we save temporary the value of Y.  */
  if (!Y_REG_P (operands[2]))
    {
      rtx ops[1];
      int y_dead = dead_register_here (insn, iy_reg);

      ops[0] = operands[1];
      if (y_dead == 0)
	{
          output_asm_insn (\"pshy\", operands);
          if (reg_mentioned_p (stack_pointer_rtx, operands[1]))
	    ops[0] = adjust_address (operands[1], GET_MODE (operands[1]), 2);
	}
      output_asm_insn (\"ldy\\t%0\", ops);
      output_asm_insn (\"bsr\\t___lshrsi3\", operands);
      return y_dead == 0 ? \"puly\" : \"\";
    }
  return \"bsr\\t___lshrsi3\";
}")

(define_expand "lshrhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(lshiftrt:HI (match_operand:HI 1 "general_operand" "")
	             (match_operand:HI 2 "general_operand" "")))]
   ""
   "
{
  if (GET_CODE (operands[2]) != CONST_INT)
    {
      rtx scratch = gen_reg_rtx (HImode);
      operand1 = force_reg (HImode, operand1);

      emit_move_insn (scratch, operands[2]);
      emit_insn (gen_rtx (PARALLEL, VOIDmode,
		 gen_rtvec (2, gen_rtx (SET, VOIDmode,
					operand0,
					gen_rtx_LSHIFTRT (HImode,
						operand1, scratch)),
			      gen_rtx (CLOBBER, VOIDmode, scratch))));
     DONE;
  }
}")

(define_insn "lshrhi3_const1"
  [(set (match_operand:HI 0 "non_push_operand" "=dm,!*u*A")
	(lshiftrt:HI (match_operand:HI 1 "non_push_operand" "0,0")
		     (const_int 1)))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  if (D_REG_P (operands[0]))
    return \"lsrd\";

  CC_STATUS_INIT;
  return \"lsr\\t%h0\\n\\tror\\t%b0\";
}")

(define_insn "lshrhi3_const"
  [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,!*A,!*A")
	(lshiftrt:HI (match_operand:HI 1 "general_operand" "dm*A,!u,dm,!u")
		     (match_operand:HI 2 "const_int_operand" "i,i,i,i")))]
  ""
  "*
{
  int val = INTVAL (operands[2]);

  if (A_REG_P (operands[0]))
    return \"#\";

  if (val >= 8)
    {
      if (val == 8)
        CC_STATUS_INIT;

      if (!H_REG_P (operands[1]))
	{
          output_asm_insn (\"clra\", operands);
          output_asm_insn (\"ldab\\t%h1\", operands);
        }
      else if (A_REG_P (operands[1]))
	{
	  output_asm_insn (\"st%1\\t%t0\", operands);
	  output_asm_insn (\"ldab\\t%t0\", operands);
	  output_asm_insn (\"clra\", operands);
	}
      else
        {
          output_asm_insn (\"tab\", operands);
          output_asm_insn (\"clra\", operands);
        }
      val -= 8;
      switch (val) 
	{
	case 7:
	  output_asm_insn (\"rolb\", operands);
	  output_asm_insn (\"tab\", operands);
	  output_asm_insn (\"rolb\", operands);
	  break;

	case 6:
	  output_asm_insn (\"rolb\", operands);
	  output_asm_insn (\"rolb\", operands);
	  output_asm_insn (\"rolb\", operands);
	  output_asm_insn (\"andb\\t#3\", operands);
	  break;

	default:
	   while (val > 0)
	     {
	        val --;
	        output_asm_insn (\"lsrb\", operands);
             }
	   break;
        }
      return \"\";
    }

  if (!D_REG_P (operands[1]))
    m68hc11_gen_movhi (insn, operands);
  switch (val)
    {
    case 7:
      output_asm_insn (\"rolb\", operands);
      output_asm_insn (\"tab\", operands);
      output_asm_insn (\"rolb\", operands);
      output_asm_insn (\"rola\", operands);
      output_asm_insn (\"rola\", operands);
      output_asm_insn (\"anda\\t#1\", operands);
      CC_STATUS_INIT;
      break;

    default:
      while (val > 0) 
	{
	  val --;
	  output_asm_insn (\"lsrd\", operands);
	}
     }
  return \"\";
}")

(define_insn "*lshrhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,*x")
	(lshiftrt:HI (match_operand:HI 1 "register_operand" "0,0")
		     (match_operand:HI 2 "register_operand" "+x,+d")))
   (clobber (match_dup 2))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  return \"bsr\\t___lshrhi3\";
}")

(define_expand "lshrqi3"
  [(set (match_operand:QI 0 "register_operand" "")
	(lshiftrt:QI (match_operand:QI 1 "register_operand" "")
	             (match_operand:QI 2 "general_operand" "")))]
   ""
   "")

(define_insn "*lshrqi3_const1"
  [(set (match_operand:QI 0 "nonimmediate_operand" "=m,d,!u,!*q,!*A")
	(lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0,0")
		     (const_int 1)))]
  ""
  "@
   lsr\\t%b0
   lsrb
   lsr\\t%b0
   lsr%0
   #")

(define_insn "*lshrqi3_const"
  [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
	(lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
		     (match_operand:QI 2 "const_int_operand" "")))]
  ""
  "*
{
  int i;
  const char* insn_code;

  if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
    insn_code = \"lsrb\";
  else if (DA_REG_P (operands[0]))
    insn_code = \"lsra\";
  else
    return \"#\";

  i = INTVAL (operands[2]);
  if (i >= 8)
    {
      if (DA_REG_P (operands[0]))
        return \"clra\";
      else
        return \"clrb\";
    }
  else if (i == 7)
    {
      if (DA_REG_P (operands[0]))
        {
          output_asm_insn (\"rola\", operands);
          output_asm_insn (\"ldaa\\t#0\", operands);
          return \"rola\";
        }
      else
        {
          output_asm_insn (\"rolb\", operands);
          output_asm_insn (\"ldab\\t#0\", operands);
          return \"rolb\";
        }
    }
  else if (i == 6)
    {
      if (DA_REG_P (operands[0]))
        {
          output_asm_insn (\"rola\", operands);
          output_asm_insn (\"rola\", operands);
          output_asm_insn (\"rola\", operands);
          return \"anda\\t#3\";
        }
      else
        {
          output_asm_insn (\"rolb\", operands);
          output_asm_insn (\"rolb\", operands);
          output_asm_insn (\"rolb\", operands);
          return \"andb\\t#3\";
        }
    }
  while (--i >= 0)
    {
      output_asm_insn (insn_code, operands);
    }
  return \"\";
}")

(define_insn "*lshrqi3"
  [(set (match_operand:QI 0 "register_operand" "=d,!*q,!*A")
	(lshiftrt:QI (match_operand:QI 1 "register_operand" "0,0,0")
		     (match_operand:QI 2 "nonimmediate_operand" 
					 "m*u*d*A,m*u*d*A,m*u")))]
  ""
  "*
{
  rtx ops[2];

  if (!D_REG_P (operands[0]) && !Q_REG_P (operands[0]))
    return \"#\";

  CC_STATUS_INIT;
  ops[0] = gen_rtx (REG, QImode, HARD_A_REGNUM);
  ops[1] = operands[2];
  m68hc11_gen_movqi (insn, ops);

  if (!optimize || optimize_size)
    {
      return \"bsr\\t___lshrqi3\";
    }

  ops[0] = gen_label_rtx ();
  ops[1] = gen_label_rtx ();
  output_asm_insn (\"ble\\t%l1\", ops);

  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
			     CODE_LABEL_NUMBER (ops[0]));

  output_asm_insn (\"lsrb\", operands);
  output_asm_insn (\"deca\", operands);
  output_asm_insn (\"bne\\t%l0\", ops);

  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, \"L\",
			     CODE_LABEL_NUMBER (ops[1]));
  return \"\";
}")

(define_insn "*rotlqi3_with_carry"
  [(set (match_operand:QI 0 "register_operand" "=d,!q")
	(rotate:QI (match_operand:QI 1 "register_operand" "0,0")
		   (reg:QI CC_REGNUM)))]
  ""
  "*
{
  if (DA_REG_P (operands[0]))
    return \"rola\";
  else
    return \"rolb\";
}")

(define_insn "*rotlhi3_with_carry"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(rotate:HI (match_operand:HI 1 "register_operand" "0")
		   (const_int 1)))
   (clobber (reg:HI CC_REGNUM))]
  ""
  "*
{
  CC_STATUS_INIT;
  return \"rolb\\n\\trola\";
}")

(define_insn "*rotrhi3_with_carry"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(rotatert:HI (match_operand:HI 1 "register_operand" "0")
		     (const_int 1)))
   (clobber (reg:HI CC_REGNUM))]
  ""
  "*
{
  CC_STATUS_INIT;
  return \"rora\\n\\trorb\";
}")

(define_insn "rotlqi3"
  [(set (match_operand:QI 0 "register_operand" "=d,!q")
	(rotate:QI (match_operand:QI 1 "register_operand" "0,0")
		   (match_operand:QI 2 "const_int_operand" "i,i")))]
  ""
  "*
{
  m68hc11_gen_rotate (ROTATE, insn, operands);
  return \"\";
}")

(define_insn "rotrqi3"
  [(set (match_operand:QI 0 "register_operand" "=d,!q")
	(rotatert:QI (match_operand:QI 1 "register_operand" "0,0")
		     (match_operand:QI 2 "const_int_operand" "i,i")))]
  ""
  "*
{
  m68hc11_gen_rotate (ROTATERT, insn, operands);
  return \"\";
}")

(define_expand "rotlhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(rotate:HI (match_operand:HI 1 "register_operand" "")
	           (match_operand:HI 2 "general_operand" "")))]
   ""
   "
{
  if (GET_CODE (operands[2]) != CONST_INT)
    {
      rtx scratch = gen_reg_rtx (HImode);
      operand1 = force_reg (HImode, operand1);

      emit_move_insn (scratch, operands[2]);
      emit_insn (gen_rtx (PARALLEL, VOIDmode,
		 gen_rtvec (2, gen_rtx (SET, VOIDmode,
					operand0,
					gen_rtx_ROTATE (HImode,
						operand1, scratch)),
			      gen_rtx (CLOBBER, VOIDmode, scratch))));
      DONE;
    }
}")

(define_insn "rotlhi3_const"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(rotate:HI (match_operand:HI 1 "register_operand" "0")
		   (match_operand:HI 2 "const_int_operand" "i")))]
  ""
  "*
{
  m68hc11_gen_rotate (ROTATE, insn, operands);
  return \"\";
}")

(define_insn "*rotlhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,*x")
	(rotate:HI (match_operand:HI 1 "register_operand" "0,0")
		   (match_operand:HI 2 "general_operand" "+x,+d")))
   (clobber (match_dup 2))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  return \"bsr\\t___rotlhi3\";
}")

(define_expand "rotrhi3"
  [(set (match_operand:HI 0 "register_operand" "")
	(rotatert:HI (match_operand:HI 1 "general_operand" "")
	             (match_operand:HI 2 "general_operand" "")))]
   ""
   "
{
  if (GET_CODE (operands[2]) != CONST_INT)
    {
      rtx scratch = gen_reg_rtx (HImode);
      operand1 = force_reg (HImode, operand1);

      emit_move_insn (scratch, operands[2]);
      emit_insn (gen_rtx (PARALLEL, VOIDmode,
		 gen_rtvec (2, gen_rtx (SET, VOIDmode,
					operand0,
					gen_rtx_ROTATERT (HImode,
						operand1, scratch)),
			      gen_rtx (CLOBBER, VOIDmode, scratch))));
      DONE;
    }
}")

(define_insn "rotrhi3_const"
  [(set (match_operand:HI 0 "register_operand" "=d")
	(rotatert:HI (match_operand:HI 1 "register_operand" "0")
		     (match_operand:HI 2 "const_int_operand" "i")))]
  ""
  "*
{
  m68hc11_gen_rotate (ROTATERT, insn, operands);
  return \"\";
}")

(define_insn "*rotrhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,*x")
	(rotatert:HI (match_operand:HI 1 "register_operand" "0,0")
		     (match_operand:HI 2 "general_operand" "+x,+d")))
   (clobber (match_dup 2))]
  ""
  "*
{
  if (A_REG_P (operands[0]))
    return \"#\";

  return \"bsr\\t___rotrhi3\";
}")

;; Split a shift operation on an address register in a shift
;; on D_REGNUM.
(define_split /* "*rotrhi3_addr" */
  [(set (match_operand:HI 0 "hard_addr_reg_operand" "")
	(match_operator:HI 3 "m68hc11_shift_operator"
	    [(match_operand:HI 1 "register_operand" "")
	     (match_operand:HI 2 "register_operand" "")]))
   (clobber (match_dup 2))]
  "z_replacement_completed == 2"
  [(parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (parallel [(set (reg:HI D_REGNUM) 
		   (match_op_dup 3 [(reg:HI D_REGNUM) (match_dup 0)]))
	      (clobber (match_dup 0))])
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  "")

;;--------------------------------------------------------------------
;;-  68HC12 Decrement/Increment and branch
;;--------------------------------------------------------------------
;; These patterns are used by loop optimization as well as peephole2
;; They must handle reloading themselves and the scratch register
;; is used for that.  Even if we accept memory operand, we must not
;; accept them on the predicate because it might create too many reloads.
;; (specially on HC12 due to its auto-incdec addressing modes).
;;
(define_expand "decrement_and_branch_until_zero"
  [(parallel [(set (pc)
		   (if_then_else
		    (ne (plus:HI (match_operand:HI 0 "register_operand" "")
				 (const_int 0))
			(const_int 1))
		    (label_ref (match_operand 1 "" ""))
		    (pc)))
	      (set (match_dup 0)
		   (plus:HI (match_dup 0)
			    (const_int -1)))
	      (clobber (match_scratch:HI 2 ""))])]
  "TARGET_M6812"
  "")

(define_expand "doloop_end"
  [(use (match_operand 0 "" ""))	; loop pseudo
   (use (match_operand 1 "" ""))	; iterations; zero if unknown
   (use (match_operand 2 "" ""))	; max iterations
   (use (match_operand 3 "" ""))	; loop level
   (use (match_operand 4 "" ""))]	; label
  "TARGET_M6812"
  "
{
  /* Reject non-constant loops as it generates bigger code due to
     the handling of the loop register.  We can do better by using
     the peephole2 dbcc/ibcc patterns.  */
  if (INTVAL (operands[1]) == 0)
    {
      FAIL;
    }
  if (GET_MODE (operands[0]) == HImode)
    {
      emit_jump_insn (gen_m68hc12_dbcc_dec_hi (operands[0],
					       gen_rtx (NE, HImode),
					       operands[4]));
      DONE;
    }
  if (GET_MODE (operands[0]) == QImode)
    {
      emit_jump_insn (gen_m68hc12_dbcc_dec_qi (operands[0],
					       gen_rtx (NE, QImode),
					       operands[4]));
      DONE;
    }

  FAIL;
}")

;; Decrement-and-branch insns.
(define_insn "m68hc12_dbcc_dec_hi"
  [(set (pc)
	(if_then_else
	  (match_operator 1 "m68hc11_eq_compare_operator"
	     [(match_operand:HI 0 "register_operand" "+dxy,m*u*z")
	      (const_int 1)])
	 (label_ref (match_operand 2 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:HI (match_dup 0) (const_int -1)))
   (clobber (match_scratch:HI 3 "=X,dxy"))]
  "TARGET_M6812"
  "*
{
  if (!H_REG_P (operands[0]))
    return \"#\";

  CC_STATUS_INIT;
  if (GET_CODE (operands[1]) == EQ)
    return \"dbeq\\t%0,%l2\";
  else
    return \"dbne\\t%0,%l2\";
}")

;; Decrement-and-branch insns.
(define_insn "m68hc12_dbcc_inc_hi"
  [(set (pc)
	(if_then_else
	  (match_operator 1 "m68hc11_eq_compare_operator"
	     [(match_operand:HI 0 "register_operand" "+dxy,m*u*z")
	      (const_int -1)])
	 (label_ref (match_operand 2 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:HI (match_dup 0) (const_int 1)))
   (clobber (match_scratch:HI 3 "=X,dxy"))]
  "TARGET_M6812"
  "*
{
  if (!H_REG_P (operands[0]))
    return \"#\";

  CC_STATUS_INIT;
  if (GET_CODE (operands[1]) == EQ)
    return \"ibeq\\t%0,%l2\";
  else
    return \"ibeq\\t%0,%l2\";
}")

;; Decrement-and-branch (QImode).
(define_insn "m68hc12_dbcc_dec_qi"
  [(set (pc)
	(if_then_else
	  (match_operator 1 "m68hc11_eq_compare_operator"
	     [(match_operand:QI 0 "register_operand" "+d,m*u*A")
	      (const_int 1)])
	 (label_ref (match_operand 2 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:QI (match_dup 0) (const_int -1)))
   (clobber (match_scratch:QI 3 "=X,d"))]
  "TARGET_M6812"
  "*
{
  if (!D_REG_P (operands[0]))
    return \"#\";

  CC_STATUS_INIT;
  if (GET_CODE (operands[1]) == EQ)
    return \"dbeq\\tb,%l2\";
  else
    return \"dbne\\tb,%l2\";
}")

;; Increment-and-branch (QImode).
(define_insn "m68hc12_dbcc_inc_qi"
  [(set (pc)
	(if_then_else
	  (match_operator 1 "m68hc11_eq_compare_operator"
	     [(match_operand:QI 0 "register_operand" "+d,m*u*A")
	      (const_int -1)])
	 (label_ref (match_operand 2 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:QI (match_dup 0) (const_int 1)))
   (clobber (match_scratch:QI 3 "=X,d"))]
  "TARGET_M6812"
  "*
{
  if (!D_REG_P (operands[0]))
    return \"#\";

  CC_STATUS_INIT;
  if (GET_CODE (operands[1]) == EQ)
    return \"ibeq\\tb,%l2\";
  else
    return \"ibeq\\tb,%l2\";
}")

;; Split the above to handle the case where operand 0 is in memory
;; (a register that couldn't get a hard register)
(define_split
  [(set (pc)
	(if_then_else
	  (match_operator 3 "m68hc11_eq_compare_operator"
	     [(match_operand:HI 0 "general_operand" "")
	      (match_operand:HI 1 "const_int_operand" "")])
	 (label_ref (match_operand 4 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:HI (match_dup 0) (match_operand 2 "const_int_operand" "")))
   (clobber (match_operand:HI 5 "hard_reg_operand" ""))]
  "TARGET_M6812 && reload_completed"
  [(set (match_dup 5) (match_dup 0))
   (set (match_dup 5) (plus:HI (match_dup 5) (match_dup 2)))
   (set (match_dup 0) (match_dup 5))
   (set (pc)
	(if_then_else (match_op_dup 3
			    [(match_dup 5) (const_int 0)])
		      (label_ref (match_dup 4)) (pc)))]
  "")

;; Split the above to handle the case where operand 0 is in memory
;; (a register that couldn't get a hard register)
(define_split
  [(set (pc)
	(if_then_else
	  (match_operator 3 "m68hc11_eq_compare_operator"
	     [(match_operand:QI 0 "general_operand" "")
	      (match_operand:QI 1 "const_int_operand" "")])
	 (label_ref (match_operand 4 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:QI (match_dup 0) (match_operand 2 "const_int_operand" "")))
   (clobber (match_operand:QI 5 "hard_reg_operand" ""))]
  "TARGET_M6812 && reload_completed"
  [(set (match_dup 5) (match_dup 0))
   (set (match_dup 5) (plus:QI (match_dup 5) (match_dup 2)))
   (set (match_dup 0) (match_dup 5))
   (set (pc)
	(if_then_else (match_op_dup 3
			    [(match_dup 5) (const_int 0)])
		      (label_ref (match_dup 4)) (pc)))]
  "")

;;--------------------------------------------------------------------
;;-  Jumps and transfers
;;--------------------------------------------------------------------
(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "bra\\t%l0")

(define_expand "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (EQ, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (NE, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (GT, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (GTU, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (LT, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (LTU, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (GE, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (GEU, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (LE, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

(define_expand "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "
{
  m68hc11_expand_compare_and_branch (LEU, m68hc11_compare_op0,
				     m68hc11_compare_op1, 
				     operands[0]);
  DONE;
}")

;;
;; Test and branch instructions for 68HC12 for EQ and NE.
;; 'z' must not appear in the constraints because the z replacement 
;; pass does not know how to restore the replacement register.
;;
(define_insn "*tbeq"
  [(set (pc)
	(if_then_else (eq (match_operand:HI 0 "register_operand" "dxy")
			  (const_int 0))
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  "TARGET_M6812"
  "*
{
   /* If the flags are already set correctly, use 'bne/beq' which are
      smaller and a little bit faster.  This happens quite often due
      to reloading of operands[0].  In that case, flags are set correctly
      due to the load instruction.  */
  if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
      || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
    return \"beq\\t%l1\";
  else
    return \"tbeq\\t%0,%l1\";
}")

(define_insn "*tbne"
  [(set (pc)
	(if_then_else (ne (match_operand:HI 0 "register_operand" "dxy")
			  (const_int 0))
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  "TARGET_M6812"
  "*
{
   if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
       || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
     return \"bne\\t%l1\";
   else
     return \"tbne\\t%0,%l1\";
}")

;;
;; Test and branch with 8-bit register.  Register must be B (or A).
;;
(define_insn "*tbeq8"
  [(set (pc)
	(if_then_else (eq (match_operand:QI 0 "register_operand" "d")
			  (const_int 0))
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  "TARGET_M6812"
  "*
{
   if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
       || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
     return \"beq\\t%l1\";
   else
     return \"tbeq\\tb,%l1\";
}")

(define_insn "*tbne8"
  [(set (pc)
	(if_then_else (ne (match_operand:QI 0 "register_operand" "d")
			  (const_int 0))
		      (label_ref (match_operand 1 "" ""))
		      (pc)))]
  "TARGET_M6812"
  "*
{
   if ((cc_status.value1 && rtx_equal_p (cc_status.value1, operands[0]))
       || (cc_status.value2 && rtx_equal_p (cc_status.value2, operands[0])))
     return \"bne\\t%l1\";
   else
     return \"tbne\\tb,%l1\";
}")

(define_insn "*beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "beq\\t%l0")

(define_insn "*bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "bne\\t%l0")

(define_insn "*bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "bgt\\t%l0")

(define_insn "*bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "bhi\\t%l0")

(define_insn "*blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_NO_OVERFLOW)
    return \"bmi\\t%l0\";
  else
    return \"blt\\t%l0\";
}")

(define_insn "*bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "blo\\t%l0")

(define_insn "*bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_NO_OVERFLOW)
    return \"bpl\\t%l0\";
  else
    return \"bge\\t%l0\";
}")

(define_insn "*bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "bhs\\t%l0")

(define_insn "*ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_NO_OVERFLOW)
    return \"bmi\\t%l0\\n\\tbeq\\t%l0\";
  else
    return \"ble\\t%l0\";
}")

(define_insn "*bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "bls\\t%l0")

;;--------------------------------------------------------------------
;;- Negative test and branch
;;--------------------------------------------------------------------
(define_insn ""
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "bne\\t%l0")

(define_insn ""
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "beq\\t%l0")

(define_insn ""
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_NO_OVERFLOW)
    return \"bmi\\t%l0\\n\\tbeq\\t%l0\";
  else
    return \"ble\\t%l0\";
}")

(define_insn ""
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "bls\\t%l0")

(define_insn ""
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_NO_OVERFLOW)
    return \"bpl\\t%l0\";
  else
    return \"bge\\t%l0\";
}")

(define_insn ""
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "bhs\\t%l0")

(define_insn ""
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "*
{
  if (cc_prev_status.flags & CC_NO_OVERFLOW)
    return \"bmi\\t%l0\";
  else
    return \"blt\\t%l0\";
}")

(define_insn ""
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "blo\\t%l0")

(define_insn ""
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "bgt\\t%l0")

(define_insn ""
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "bhi\\t%l0")

;;--------------------------------------------------------------------
;;-  Calls
;;--------------------------------------------------------------------
;;
;;- Call a function that returns no value.
(define_insn "call"
  [(call (match_operand:QI 0 "memory_operand" "m")
	 (match_operand:SI 1 "general_operand" "g"))]
  ;; Operand 1 not really used on the m68hc11.
  ""
 "*
{
  if (GET_CODE (XEXP (operands[0], 0)) == SYMBOL_REF)
    {
      if (m68hc11_is_far_symbol (operands[0]))
        {
          if (TARGET_M6812)
            {
	      output_asm_insn (\"call\\t%0\", operands);
	      return \"\";
	    }
          else
	    {
	      output_asm_insn (\"pshb\", operands);
	      output_asm_insn (\"ldab\\t#%%page(%0)\", operands);
	      output_asm_insn (\"ldy\\t#%%addr(%0)\", operands);
	      return \"jsr\\t__call_a32\";
	    }
	}
      if (m68hc11_is_trap_symbol (operands[0]))
        return \"swi\";
      else
        return \"bsr\\t%0\";
    }
  else
    {
      return \"jsr\\t%0\";
    }
}")

(define_insn "call_value"
  [(set (match_operand 0 "" "=g")
	(call (match_operand:QI 1 "memory_operand" "m")
	      (match_operand:SI 2 "general_operand" "g")))]
  ""
 "*
{
  if (GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
    {
      if (m68hc11_is_far_symbol (operands[1]))
        {
          if (TARGET_M6812)
            {
	      output_asm_insn (\"call\\t%1\", operands);
	      return \"\";
	    }
          else
	    {
	      output_asm_insn (\"pshb\", operands);
	      output_asm_insn (\"ldab\\t#%%page(%1)\", operands);
	      output_asm_insn (\"ldy\\t#%%addr(%1)\", operands);
	      return \"jsr\\t__call_a32\";
	    }
	}
      if (m68hc11_is_trap_symbol (operands[1]))
        return \"swi\";
      else
        return \"bsr\\t%1\";
    }
  else
    {
      return \"jsr\\t%1\";
    }
}")

;; Call subroutine returning any type.

(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));
    }

  /* The optimizer does not know that the call sets the function value
     registers we stored in the result block.  We avoid problems by
     claiming that all hard registers are used and clobbered at this
     point.  */
  emit_insn (gen_blockage ());

  DONE;
}")

;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and
;; all of memory.  This blocks insns from being moved across this point.

(define_insn "blockage"
  [(unspec_volatile [(const_int 0)] 0)]
  ""
  "")

(define_insn "nop"
  [(const_int 0)]
  ""
  "nop")
    
(define_expand "prologue"
  [(const_int 0)]
  ""
  "
{
  expand_prologue (); 
  DONE;
}")

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

;; Used for frameless functions which save no regs and allocate no locals.
(define_expand "return"
  [(return)]
  "reload_completed && m68hc11_total_frame_size () == 0"
  "
{
  int ret_size = 0;

  if (current_function_return_rtx)
    ret_size = GET_MODE_SIZE (GET_MODE (current_function_return_rtx));

  /* Emit use notes only when HAVE_return is true.  */
  if (m68hc11_total_frame_size () != 0)
    ret_size = 0;

  if (ret_size && ret_size <= 2)
    {
      emit_jump_insn (gen_rtx (PARALLEL, VOIDmode,
		      gen_rtvec (2, gen_rtx_RETURN (VOIDmode),
				 gen_rtx_USE (VOIDmode,
				       	      gen_rtx_REG (HImode, 1)))));
      DONE;
    }
  if (ret_size)
    {
      emit_jump_insn (gen_rtx (PARALLEL, VOIDmode,
		      gen_rtvec (2, gen_rtx_RETURN (VOIDmode),
			         gen_rtx_USE (VOIDmode,
					      gen_rtx_REG (SImode, 0)))));
      DONE;
    }
}")

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

  if (next
      && GET_CODE (next) == JUMP_INSN
      && GET_CODE (PATTERN (next)) == RETURN)
    return \"\";
  if (current_function_interrupt || current_function_trap)
    return \"rti\";
  else if (!current_function_far)
    return \"rts\";
  else if (TARGET_M6812)
    return \"rtc\";
  else
    {
      int ret_size = 0;

      if (current_function_return_rtx)
        ret_size = GET_MODE_SIZE (GET_MODE (current_function_return_rtx));

      if (ret_size == 0)
        return \"jmp\\t__return_void\";
      if (ret_size <= 2)
        return \"jmp\\t__return_16\";
      if (ret_size <= 4)
        return \"jmp\\t__return_32\";
      return \"jmp\\t__return_16\";
    }
}")

(define_insn "*return_16bit"
  [(return)
   (use (reg:HI D_REGNUM))]
  "reload_completed && m68hc11_total_frame_size () == 0"
  "*
{
  rtx next = next_active_insn (insn);

  if (next
      && GET_CODE (next) == JUMP_INSN
      && GET_CODE (PATTERN (next)) == RETURN)
    return \"\";
  if (current_function_interrupt || current_function_trap)
    return \"rti\";
  else if (!current_function_far)
    return \"rts\";
  else if (TARGET_M6812)
    return \"rtc\";
  else
    return \"jmp\\t__return_16\";
}")

(define_insn "*return_32bit"
  [(return)
   (use (reg:SI 0))]
  "reload_completed && m68hc11_total_frame_size () == 0"
  "*
{
  rtx next = next_active_insn (insn);

  if (next
      && GET_CODE (next) == JUMP_INSN
      && GET_CODE (PATTERN (next)) == RETURN)
    return \"\";
  if (current_function_interrupt || current_function_trap)
    return \"rti\";
  else if (!current_function_far)
    return \"rts\";
  else if (TARGET_M6812)
    return \"rtc\";
  else
    return \"jmp\\t__return_32\";
}")

(define_insn "indirect_jump"
  [(set (pc) (match_operand:HI 0 "nonimmediate_operand" "xy"))]
  ""
  "jmp\\t0,%0")

;;--------------------------------------------------------------------
;;-  Table jump
;;--------------------------------------------------------------------
;;
;; Operand 0 is the address of the table element to use
;; operand 1 is the CODE_LABEL for the table
;;--------------------------------------------------------------------
(define_expand "tablejump"
  [(parallel [(set (pc) (match_operand 0 "" ""))
	      (use (label_ref (match_operand 1 "" "")))])]
  ""
  "")

(define_insn "*jump_indirect"
   [(parallel [
	(set (pc) (match_operand:HI 0 "register_operand" "xy"))
	(use (label_ref (match_operand 1 "" "")))])]
   ""
  "jmp\\t0,%0")

;;--------------------------------------------------------------------
;;- Peepholes
;;--------------------------------------------------------------------

;;--------------------------------------------------------------------
;;- 68HC12 dbcc/ibcc peepholes
;;--------------------------------------------------------------------
;;
;; Replace: "addd #-1; bne L1" into "dbne d,L1"
;;          "addd #-1; beq L1" into "dbeq d,L1"
;;          "addd #1; bne L1" into "ibne d,L1"
;;          "addd #1; beq L1" into "ibeq d,L1"
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(plus:HI (match_dup 0)
	         (match_operand:HI 1 "const_int_operand" "")))
   (set (pc)
        (if_then_else (match_operator 2 "m68hc11_eq_compare_operator"
		         [(match_dup 0)
			  (const_int 0)])
		      (label_ref (match_operand 3 "" "")) (pc)))]
  "TARGET_M6812 && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)"
  [(parallel [
      (set (pc) (if_then_else (match_op_dup 2 [(match_dup 0) (match_dup 5)])
			      (label_ref (match_dup 3)) (pc)))
      (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))
      (clobber (match_dup 4))])]
  "operands[4] = gen_rtx_SCRATCH(HImode);
   operands[5] = GEN_INT (-INTVAL (operands[1]));")


;;
;; Replace: "addb #-1; bne L1" into "dbne b,L1"
;;          "addb #-1; beq L1" into "dbeq b,L1"
;;
(define_peephole2
  [(set (match_operand:QI 0 "hard_reg_operand" "")
	(plus:QI (match_dup 0)
	         (match_operand:QI 1 "const_int_operand" "")))
   (set (pc)
        (if_then_else (match_operator 2 "m68hc11_eq_compare_operator"
		         [(match_dup 0)
			  (const_int 0)])
		      (label_ref (match_operand 3 "" "")) (pc)))]
  "TARGET_M6812 && D_REG_P (operands[0])
   && (INTVAL (operands[1]) == 1 || INTVAL (operands[1]) == -1)"
  [(parallel [
      (set (pc) (if_then_else (match_op_dup 2 [(match_dup 0) (match_dup 5)])
			      (label_ref (match_dup 3)) (pc)))
      (set (match_dup 0) (plus:QI (match_dup 0) (match_dup 1)))
      (clobber (match_dup 4))])]
  "operands[4] = gen_rtx_SCRATCH(QImode);
   operands[5] = GEN_INT (-INTVAL (operands[1]));")


;;--------------------------------------------------------------------
;;- Move peephole2
;;--------------------------------------------------------------------

;;
;; Replace "leas 2,sp" with a "pulx" or a "puly".
;; On 68HC12, this is one cycle slower but one byte smaller.
;; pr target/6899: This peephole is not valid because a register CSE
;; pass removes the pulx/puly.
;;
(define_peephole2
  [(set (reg:HI SP_REGNUM) (plus:HI (reg:HI SP_REGNUM) (const_int 2)))
   (match_scratch:HI 0 "xy")]
  "0 && TARGET_M6812 && optimize_size"
  [(set (match_dup 0) (match_dup 1))]
  "operands[1] = gen_rtx (MEM, HImode,
			  gen_rtx (POST_INC, HImode,
				   gen_rtx_REG (HImode, HARD_SP_REGNUM)));")

;;
;; Optimize memory<->memory moves when the value is also loaded in
;; a register.
;;
(define_peephole2
  [(set (match_operand:QI 0 "memory_operand" "")
	(match_operand:QI 1 "memory_operand" ""))
   (set (reg:QI D_REGNUM)
	(match_operand:QI 2 "memory_operand" ""))]
  "(rtx_equal_p (operands[0], operands[2]) && !side_effects_p (operands[0]))
   || (GET_CODE (XEXP (operands[0], 0)) == REG
       && GET_CODE (XEXP (operands[2], 0)) == POST_INC
       && rtx_equal_p (XEXP (operands[0], 0), XEXP (XEXP (operands[2], 0), 0)))"
  [(set (reg:QI D_REGNUM) (match_dup 1))
   (set (match_dup 2) (reg:QI D_REGNUM))]
  "")

;;
;; Remove a possible move before a compare instruction when that
;; move will go in a dead register.  Compare with the source then.
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(match_operand:HI 1 "hard_reg_operand" ""))
   (set (cc0)
	(compare (match_dup 0)
	         (match_operand:HI 2 "cmp_operand" "")))]
  "(X_REG_P (operands[1]) || Y_REG_P (operands[1]))
   && peep2_reg_dead_p (2, operands[0])
   && !reg_mentioned_p (operands[0], operands[2])"
  [(set (cc0) (compare (match_dup 1) (match_dup 2)))]
  "")

;;
;; Optimize loading a constant to memory when that same constant
;; is loaded to a hard register.  Switch the two to use the register
;; for memory initialization.  In most cases, the constant is 0.
;;
(define_peephole2
  [(set (match_operand:HI 0 "memory_operand" "")
	(match_operand:HI 1 "immediate_operand" ""))
   (set (match_operand:HI 2 "hard_reg_operand" "")
        (match_dup 1))]
  "(D_REG_P (operands[2]) || X_REG_P (operands[2]) || Y_REG_P (operands[2]))
   && !reg_mentioned_p (operands[2], operands[0])"
  [(set (match_dup 2) (match_dup 1))
   (set (match_dup 0) (match_dup 2))]
  "")

;;
;; Reorganize to optimize address computations.
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(match_operand:HI 1 "const_int_operand" ""))
   (set (match_dup 0)
	(plus:HI (match_dup 0)
	         (match_operand:HI 2 "general_operand" "")))]
  "(INTVAL (operands[1]) >= -2 && INTVAL (operands[1]) <= 2)"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
  "")

;;
;; Optimize an address register increment and a compare to use
;; a PRE_INC or PRE_DEC addressing mode (disabled on the tst insn
;; before reload, but can be enabled after).
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(plus:HI (match_dup 0)
	         (match_operand:HI 1 "const_int_operand" "")))
   (set (cc0)
	(match_operand:QI 2 "memory_operand" ""))]
  "TARGET_AUTO_INC_DEC
   && (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
   && reg_mentioned_p (operands[0], operands[2])"
  [(set (cc0) (match_dup 3))]
  "if (INTVAL (operands[1]) == 1)
     operands[3] = gen_rtx (MEM, QImode,
			    gen_rtx (PRE_INC, HImode, operands[0]));
   else
     operands[3] = gen_rtx (MEM, QImode,
			    gen_rtx (PRE_DEC, HImode, operands[0]));
  ")

;;
;; Likewise for compare.
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(plus:HI (match_dup 0)
	         (match_operand:HI 1 "const_int_operand" "")))
   (set (cc0)
	(compare (match_operand:QI 2 "hard_reg_operand" "")
		 (match_operand:QI 3 "memory_operand" "")))]
  "TARGET_AUTO_INC_DEC
   && (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
   && reg_mentioned_p (operands[0], operands[3])"
  [(set (cc0) (compare (match_dup 2) (match_dup 4)))]
  "if (INTVAL (operands[1]) == 1)
     operands[4] = gen_rtx (MEM, QImode,
			    gen_rtx (PRE_INC, HImode, operands[0]));
   else
     operands[4] = gen_rtx (MEM, QImode,
			    gen_rtx (PRE_DEC, HImode, operands[0]));
  ")

(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(plus:HI (match_dup 0)
	         (match_operand:HI 1 "const_int_operand" "")))
   (set (cc0)
	(compare (match_operand:QI 2 "memory_operand" "")
		 (match_operand:QI 3 "hard_reg_operand" "")))]
  "TARGET_AUTO_INC_DEC
   && (INTVAL (operands[1]) == -1 || INTVAL (operands[1]) == 1)
   && reg_mentioned_p (operands[0], operands[2])"
  [(set (cc0) (compare (match_dup 4) (match_dup 3)))]
  "if (INTVAL (operands[1]) == 1)
     operands[4] = gen_rtx (MEM, QImode,
			    gen_rtx (PRE_INC, HImode, operands[0]));
   else
     operands[4] = gen_rtx (MEM, QImode,
			    gen_rtx (PRE_DEC, HImode, operands[0]));
  ")

;;
;; Replace a "ldx #N; addx <sp>" with a "ldx <sp>; addx #n"
;; (avoids many temporary moves because we can't add sp to another reg easily)
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
        (match_operand:HI 1 "const_int_operand" ""))
   (set (match_dup 0) (plus:HI (match_dup 0) (reg:HI SP_REGNUM)))]
  ""
  [(set (match_dup 0) (reg:HI SP_REGNUM))
   (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
  "")

;;
;; Replace "ldd #N; addd <op>" with "ldd <op>; addd #N".
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(match_operand:HI 1 "const_int_operand" ""))
   (set (match_dup 0)
	(plus:HI (match_dup 0)
	         (match_operand:HI 2 "general_operand" "")))]
  "(INTVAL (operands[1]) >= -2 && INTVAL (operands[1]) <= 2)"
  [(set (match_dup 0) (match_dup 2))
   (set (match_dup 0) (plus:HI (match_dup 0) (match_dup 1)))]
  "")

;;
;; Replace a "ldd <mem>; psha; pshb" with a "ldx <mem>; pshx".
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
        (match_operand:HI 1 "memory_operand" ""))
   (set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM)))
        (match_dup 0))
   (match_scratch:HI 2 "x")]
  "TARGET_M6811 && D_REG_P (operands[0]) && peep2_reg_dead_p (2, operands[0])"
  [(set (match_dup 2) (match_dup 1))
   (set (mem:HI (pre_dec:HI (reg:HI SP_REGNUM))) (match_dup 2))]
  "")

;;
;; Replace a "ldd <mem>; addd #N; std <mem>" into a
;; "ldx <mem>; leax; stx <mem>" if we have a free X/Y register
;; and the constant is small.
;;
(define_peephole2
  [(set (match_operand:HI 0 "hard_reg_operand" "")
	(match_operand:HI 1 "general_operand" ""))
   (set (match_dup 0) (plus:HI (match_dup 0)
			       (match_operand:HI 2 "const_int_operand" "")))
   (set (match_operand:HI 3 "nonimmediate_operand" "")
        (match_dup 0))
   (match_scratch:HI 4 "xy")]
  "D_REG_P (operands[0])
   && (TARGET_M6812 
       || (INTVAL (operands[2]) >= -2 && INTVAL (operands[2]) <= 2))
   && peep2_reg_dead_p (3, operands[0])"
  [(set (match_dup 4) (match_dup 1))
   (set (match_dup 4) (plus:HI (match_dup 4) (match_dup 2)))
   (set (match_dup 3) (match_dup 4))]
  "if (reg_mentioned_p (operands[4], operands[1])) FAIL;
   if (reg_mentioned_p (operands[4], operands[3])) FAIL;")

;;
;; This peephole catches the address computations generated by the reload
;; pass. 
(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "xy")
	(match_operand:HI 1 "const_int_operand" ""))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
	      (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:HI D_REGNUM)
	(plus (reg:HI D_REGNUM)
	      (match_operand:HI 2 "general_operand" "")))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
	      (set (match_dup 0) (reg:HI D_REGNUM))])]
  "(INTVAL (operands[1]) & 0x0FF) == 0"
  "*
{
  int value_loaded = 1;

  if (X_REG_P (operands[0]) || SP_REG_P (operands[2]))
    {
      rtx ops[2];

      ops[0] = operands[0];
      ops[1] = operands[2];
      m68hc11_gen_movhi (insn, ops);
      output_asm_insn (\"xgd%0\", operands);
    }
  else if (Y_REG_P (operands[0]))
    {
      if (reg_mentioned_p (iy_reg, operands[2]))
        output_asm_insn (\"ldy\\t%2\", operands);
      else
	value_loaded = 0;
      output_asm_insn (\"xgdy\", operands);
    }
  else
    {
      output_asm_insn (\"ldd\\t%2\", operands);
    }

  if (value_loaded == 0)
    output_asm_insn (\"ldd\\t%2\", operands);
  if ((INTVAL (operands[1]) & 0x0ff00) == 0x100)
    output_asm_insn (\"inca\", operands);
  else if ((INTVAL (operands[1]) & 0x0ff00) == 0xff00)
    output_asm_insn (\"deca\", operands);
  else if (INTVAL (operands[1]) != 0)
    output_asm_insn (\"adda\\t%h1\", operands);

  if (X_REG_P (operands[0]))
    return \"xgdx\";
  else if (Y_REG_P (operands[0]))
    return \"xgdy\";
  else
    return \"\";
}
")

(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "h")
	(match_operand:HI 1 "non_push_operand" "g"))
   (set (match_operand:HI 2 "hard_reg_operand" "h")
        (match_dup 0))]
  "find_regno_note (insn, REG_DEAD, REGNO (operands[0]))
   && !S_REG_P (operands[2])"
  "*
{
  rtx ops[2];

  ops[0] = operands[2];
  ops[1] = operands[1];
  m68hc11_gen_movhi (insn, ops);
  return \"\";
}
")

(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "h")
	(match_operand:HI 1 "hard_reg_operand" "h"))
   (set (match_operand:HI 2 "non_push_operand" "g")
        (match_dup 0))]
  "find_regno_note (insn, REG_DEAD, REGNO (operands[0]))
   && !S_REG_P (operands[2])"
  "*
{
  rtx ops[2];

  ops[0] = operands[2];
  ops[1] = operands[1];
  m68hc11_gen_movhi (insn, ops);
  return \"\";
}
")

;;
;; Catch a (set X/Y D) followed by a swap. In this form, D is dead after
;; the set, so we don't need to emit anything. 'ins1' refers to the
;; (set ...) insn.
;;
(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  "find_regno_note (ins1, REG_DEAD, HARD_D_REGNUM)"
  "*
{
   cc_status = cc_prev_status;
   return \"\";
}
")

;; Same as above but due to some split, there may be a noop set
;; between the two.
(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
   (set (match_dup 0) (match_dup 0))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  "find_regno_note (ins1, REG_DEAD, HARD_D_REGNUM)"
  "*
{
   cc_status = cc_prev_status;
   return \"\";
}
")

;;
;; Catch a (set X/Y D) followed by an xgdx/xgdy. D is not dead
;; and we must, at least, setup X/Y with value of D.
;;
(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "A") (reg:HI D_REGNUM))
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  ""
  "*
{
  rtx ops[2];

  ops[0] = operands[0];
  ops[1] = gen_rtx (REG, HImode, HARD_D_REGNUM);
  m68hc11_gen_movhi (insn, ops);
  return \"\";
}
")

;;;
;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
;;; need to emit anything. Otherwise, we just need an copy of D to X/Y.
;;;
(define_peephole
  [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:HI D_REGNUM) (match_dup 0))]
  "find_regno_note (insn, REG_DEAD, REGNO (operands[0]))"
  "*
{
  cc_status = cc_prev_status;
  return \"\";
}
")

;;;
;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
;;; need to emit anything. Otherwise, we just need an copy of D to X/Y.
;;;
(define_peephole
  [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:QI D_REGNUM) (match_operand:QI 1 "hard_reg_operand" "A"))]
  "REGNO (operands[0]) == REGNO (operands[1])
   && find_regno_note (insn, REG_DEAD, REGNO (operands[0]))"
  "*
{
  cc_status = cc_prev_status;
  return \"\";
}
")

;;;
;;; Catch an xgdx/xgdy followed by a (set D X/Y). If X/Y is dead, we don't
;;; need to emit anything. Otherwise, we just need a copy of D to X/Y.
;;;
(define_peephole
  [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:HI D_REGNUM) (match_dup 0))]
  ""
  "*
{
  rtx ops[2];

  ops[0] = operands[0];
  ops[1] = gen_rtx (REG, HImode, HARD_D_REGNUM);
  m68hc11_gen_movhi (insn, ops);
  return \"\";
}
")

;;;
;;; Same peephole with a QI set.  The copy is made as 16-bit to comply
;;; with the xgdx.
;;;
(define_peephole
  [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (set (reg:QI D_REGNUM) (match_operand:QI 1 "hard_reg_operand" "A"))]
  "REGNO (operands[0]) == REGNO (operands[1])"
  "*
{
  rtx ops[2];

  ops[0] = operands[0];
  ops[1] = gen_rtx (REG, HImode, HARD_D_REGNUM);
  m68hc11_gen_movhi (insn, ops);
  return \"\";
}
")

;;;
;;; Catch two consecutive xgdx or xgdy, emit nothing.
;;;
(define_peephole
  [(parallel [(set (reg:HI D_REGNUM) (match_operand:HI 0 "hard_reg_operand" "A"))
              (set (match_dup 0) (reg:HI D_REGNUM))])
   (parallel [(set (reg:HI D_REGNUM) (match_dup 0))
              (set (match_dup 0) (reg:HI D_REGNUM))])]
  ""
  "*
{
  cc_status = cc_prev_status;
  return \"\";
}
")

(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "")
        (match_operand:HI 1 "stack_register_operand" ""))
   (set (match_operand:HI 2 "hard_reg_operand" "")
	(match_operand:HI 3 "memory_operand" "m"))
   (set (match_dup 0)
        (match_operand:HI 4 "memory_operand" "m"))]
  "IS_STACK_POP (operands[4])
   && (GET_CODE (operands[3]) == MEM &&
       rtx_equal_p (operands[0], XEXP (operands[3], 0)))"
  "*
{
  rtx ops[2];

  ops[0] = operands[2];
  ops[1] = gen_rtx (MEM, HImode,
		    gen_rtx (POST_INC, HImode, stack_pointer_rtx));
  m68hc11_gen_movhi (insn, ops);
  return \"\";
}
")

;;
;; Catch (d = -1) (d = d + sp) to avoid 2 adjust of SP.
;;
(define_peephole
  [(set (match_operand:HI 0 "hard_reg_operand" "dA") (const_int -1))
   (set (match_dup 0) (plus:HI (match_dup 0) (reg:HI SP_REGNUM)))]
  "TARGET_M6811"
  "*
{
  return \"sts\\t%t0\\n\\tld%0\\t%t0\";
}
")
