OSDN Git Service

* config/m68hc11/m68hc11.md: New file, machine description for
authorciceron <ciceron@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 8 Sep 2000 20:54:44 +0000 (20:54 +0000)
committerciceron <ciceron@138bc75d-0d04-0410-961f-82ee72b054a4>
Fri, 8 Sep 2000 20:54:44 +0000 (20:54 +0000)
68HC11 & 68HC12.
* config/m68hc11/m68hc11.h: New file, definitions for 68HC11 & 68HC12.
* config/m68hc11/m68hc11.c: New file, functions for 68HC11 & 68HC12.
* config/m68hc11/m68hc12.h: New file, definitions for 68HC12.
* config/m68hc11/m68hc11-protos.h: New file.
* config/m68hc11/m68hc11-crt0.S: New file, startup code.
* config/m68hc11/t-m68hc11-gas: New file, makefile fragment.
* config/m68hc11/xm-m68hc11.h: New file, target defs.
* config/m68hc11/larith.asm: New file, libgcc routines.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@36276 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/m68hc11/larith.asm [new file with mode: 0644]
gcc/config/m68hc11/m68hc11-crt0.S [new file with mode: 0644]
gcc/config/m68hc11/m68hc11-protos.h [new file with mode: 0644]
gcc/config/m68hc11/m68hc11.c [new file with mode: 0644]
gcc/config/m68hc11/m68hc11.h [new file with mode: 0644]
gcc/config/m68hc11/m68hc11.md [new file with mode: 0644]
gcc/config/m68hc11/m68hc12.h [new file with mode: 0644]
gcc/config/m68hc11/t-m68hc11-gas [new file with mode: 0644]
gcc/config/m68hc11/xm-m68hc11.h [new file with mode: 0644]

index 1e5446d..bfdf06d 100644 (file)
@@ -1,5 +1,18 @@
 2000-09-08  Stephane Carrez  <Stephane.Carrez@worldnet.fr>
 
+       * config/m68hc11/m68hc11.md: New file, machine description for
+       68HC11 & 68HC12.
+       * config/m68hc11/m68hc11.h: New file, definitions for 68HC11 & 68HC12.
+       * config/m68hc11/m68hc11.c: New file, functions for 68HC11 & 68HC12.
+       * config/m68hc11/m68hc12.h: New file, definitions for 68HC12.
+       * config/m68hc11/m68hc11-protos.h: New file.
+       * config/m68hc11/m68hc11-crt0.S: New file, startup code.
+       * config/m68hc11/t-m68hc11-gas: New file, makefile fragment.
+       * config/m68hc11/xm-m68hc11.h: New file, target defs.
+       * config/m68hc11/larith.asm: New file, libgcc routines.
+
+2000-09-08  Stephane Carrez  <Stephane.Carrez@worldnet.fr>
+
        * Makefile.in (DPBIT_FUNCS): Add _usi_to_df.
        (FPBIT_FUNCS): Add _usi_to_sf.
        * config/fp-bit.c (usi_to_float): New function.
diff --git a/gcc/config/m68hc11/larith.asm b/gcc/config/m68hc11/larith.asm
new file mode 100644 (file)
index 0000000..e757729
--- /dev/null
@@ -0,0 +1,968 @@
+/* libgcc1 routines for M68HC11.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file with other programs, and to distribute
+those programs without any restriction coming from the use of this
+file.  (The General Public License restrictions do apply in other
+respects; for example, they cover modification of the file, and
+distribution when not linked into another program.)
+
+This file 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 this program; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with other files,
+   some of which are compiled with GCC, to produce an executable,
+   this library does not by itself cause the resulting executable
+   to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+
+       .file "larith.asm"
+
+       .sect .text
+       
+
+#define REG(NAME)                      \
+NAME:  .word 0;                        \
+       .type NAME,@object ;            \
+       .size NAME,2
+
+#ifdef L_regs_min
+/* Pseudo hard registers used by gcc.
+   They must be located in page0. 
+   They will normally appear at the end of .page0 section.  */
+       .sect .page0
+       .globl _.tmp,_.frame
+       .globl _.z,_.xy
+REG(_.tmp)
+REG(_.z)
+REG(_.xy)
+REG(_.frame)
+
+#endif
+
+#ifdef L_regs_d1_8
+/* Pseudo hard registers used by gcc.
+   They must be located in page0. 
+   They will normally appear at the end of .page0 section.  */
+       .sect .page0
+       .globl _.d1,_.d2,_.d3,_.d4,_.d5,_.d6
+       .globl _.d7,_.d8
+REG(_.d1)
+REG(_.d2)
+REG(_.d3)
+REG(_.d4)
+REG(_.d5)
+REG(_.d6)
+REG(_.d7)
+REG(_.d8)
+
+#endif
+
+#ifdef L_regs_d8_16
+/* Pseudo hard registers used by gcc.
+   They must be located in page0. 
+   They will normally appear at the end of .page0 section.  */
+       .sect .page0
+       .globl _.d9,_.d10,_.d11,_.d12,_.d13,_.d14
+       .globl _.d15,_.d16
+REG(_.d9)
+REG(_.d10)
+REG(_.d11)
+REG(_.d12)
+REG(_.d13)
+REG(_.d14)
+REG(_.d15)
+REG(_.d16)
+
+#endif
+
+#ifdef L_regs_d17_32
+/* Pseudo hard registers used by gcc.
+   They must be located in page0. 
+   They will normally appear at the end of .page0 section.  */
+       .sect .page0
+       .globl _.d17,_.d18,_.d19,_.d20,_.d21,_.d22
+       .globl _.d23,_.d24,_.d25,_.d26,_.d27,_.d28
+       .globl _.d29,_.d30,_.d31,_.d32
+REG(_.d17)
+REG(_.d18)
+REG(_.d19)
+REG(_.d20)
+REG(_.d21)
+REG(_.d22)
+REG(_.d23)
+REG(_.d24)
+REG(_.d25)
+REG(_.d26)
+REG(_.d27)
+REG(_.d28)
+REG(_.d29)
+REG(_.d30)
+REG(_.d31)
+REG(_.d32)
+#endif
+
+#ifdef L_premain
+;;
+;; Specific initialization for 68hc11 before the main.
+;; Nothing special for a generic routine; Just enable interrupts.
+;;
+       .sect .text
+       .globl __premain
+__premain:
+       clra
+       tap     ; Clear both I and X.
+       rts
+#endif
+
+#ifdef L__exit
+;;
+;; Exit operation.  Just loop forever and wait for interrupts.
+;; (no other place to go)
+;;
+       .sect .text
+       .globl _exit    
+       .globl exit
+       .weak  exit
+exit:
+_exit:
+fatal:
+       cli
+       wai
+       bra fatal
+#endif
+
+#ifdef L_abort
+;;
+;; Abort operation.  This is defined for the GCC testsuite.
+;;
+       .sect .text
+       .globl abort
+abort:
+       ldd     #255            ; 
+       .byte 0xCD              ; Generate an illegal instruction trap
+       .byte 0x03              ; The simulator catches this and stops.
+       jmp _exit
+#endif
+       
+#ifdef L_cleanup
+;;
+;; Cleanup operation used by exit().
+;;
+       .sect .text
+       .globl _cleanup
+_cleanup:
+       rts
+#endif
+
+;-----------------------------------------
+; required gcclib code
+;-----------------------------------------
+#ifdef L_memcpy
+       .sect .text
+       .weak memcpy
+       .globl memcpy
+       .globl __memcpy
+;;;
+;;; void* memcpy(void*, const void*, size_t)
+;;; 
+;;; D    = dst Pmode
+;;; 2,sp = src Pmode
+;;; 4,sp = size        HImode (size_t)
+;;; 
+__memcpy:
+memcpy:
+       xgdy
+       tsx
+       ldd     4,x
+       ldx     2,x             ; SRC = X, DST = Y
+       cpd     #0
+       beq     End
+       pshy
+       inca                    ; Correction for the deca below
+L0:
+       psha                    ; Save high-counter part
+L1:
+       ldaa    0,x             ; Copy up to 256 bytes
+       staa    0,y
+       inx
+       iny
+       decb
+       bne     L1
+       pula
+       deca
+       bne     L0
+       puly                    ; Restore Y to return the DST
+End:
+       xgdy
+       rts
+#endif
+
+#ifdef L_memset
+       .sect .text
+       .globl memset
+       .globl __memset
+;;;
+;;; void* memset(void*, int value, size_t)
+;;; 
+#ifndef __HAVE_SHORT_INT__
+;;; D    = dst Pmode
+;;; 2,sp = src SImode
+;;; 6,sp = size        HImode (size_t)
+       val  = 5
+       size = 6
+#else
+;;; D    = dst Pmode
+;;; 2,sp = src SImode
+;;; 6,sp = size        HImode (size_t)
+       val  = 3
+       size = 4
+#endif
+__memset:
+memset:
+       xgdx
+       tsy
+       ldab    val,y
+       ldy     size,y          ; DST = X, CNT = Y
+       beq     End
+       pshx
+L0:
+       stab    0,x             ; Fill up to 256 bytes
+       inx
+       dey
+       bne     L0
+       pulx                    ; Restore X to return the DST
+End:
+       xgdx
+       rts
+#endif
+               
+#ifdef L_adddi3
+       .sect .text
+       .globl ___adddi3
+
+___adddi3:
+       tsx
+       tsy
+       pshb
+       psha
+       ldd     8,x
+       addd    16,y
+       pshb
+       psha
+
+       ldd     6,x
+       adcb    15,y
+       adca    14,y
+       pshb
+       psha
+
+       ldd     4,x
+       adcb    13,y
+       adca    12,y
+       pshb
+       psha
+       
+       ldd     2,x
+       adcb    11,y
+       adca    10,y
+       tsx
+       ldy     6,x
+
+       std     0,y
+       pulx
+       stx     2,y
+       pulx
+       stx     4,y
+       pulx
+       stx     6,y
+       pulx
+       rts
+#endif
+
+#ifdef L_subdi3
+       .sect .text
+       .globl ___subdi3
+
+___subdi3:
+       tsx
+       tsy
+       pshb
+       psha
+       ldd     8,x
+       subd    16,y
+       pshb
+       psha
+
+       ldd     6,x
+       sbcb    15,y
+       sbca    14,y
+       pshb
+       psha
+
+       ldd     4,x
+       sbcb    13,y
+       sbca    12,y
+       pshb
+       psha
+       
+       ldd     2,x
+       sbcb    11,y
+       sbca    10,y
+       
+       tsx
+       ldy     6,x
+
+       std     0,y
+       pulx
+       stx     2,y
+       pulx
+       stx     4,y
+       pulx
+       stx     6,y
+       pulx
+       rts
+#endif
+       
+#ifdef L_notdi2
+       .sect .text
+       .globl ___notdi2
+
+___notdi2:
+       tsy
+       xgdx
+       ldd     8,y
+       coma
+       comb
+       std     6,x
+       
+       ldd     6,y
+       coma
+       comb
+       std     4,x
+
+       ldd     4,y
+       coma
+       comb
+       std     2,x
+
+       ldd     2,y
+       coma
+       comb
+       std     0,x
+       rts
+#endif
+       
+#ifdef L_negsi2
+       .sect .text
+       .globl ___negsi2
+
+___negsi2:
+       comb
+       coma
+       addd    #1
+       xgdx
+       eorb    #0xFF
+       eora    #0xFF
+       adcb    #0
+       adca    #0
+       xgdx
+       rts
+#endif
+
+#ifdef L_one_cmplsi2
+       .sect .text
+       .globl ___one_cmplsi2
+
+___one_cmplsi2:
+       comb
+       coma
+       xgdx
+       comb
+       coma
+       xgdx
+       rts
+#endif
+       
+#ifdef L_ashlsi3
+       .sect .text
+       .globl ___ashlsi3
+
+___ashlsi3:
+       xgdy
+       clra
+       andb    #0x1f
+       xgdy
+       beq     Return
+Loop:
+       lsld
+       xgdx
+       rolb
+       rola
+       xgdx
+       dey
+       bne     Loop
+Return:
+       rts
+#endif
+
+#ifdef L_ashrsi3
+       .sect .text
+       .globl ___ashrsi3
+
+___ashrsi3:
+       xgdy
+       clra
+       andb    #0x1f
+       xgdy
+       beq     Return
+Loop:
+       xgdx
+       asra
+       rorb
+       xgdx
+       rora
+       rorb
+       dey
+       bne     Loop
+Return:
+       rts
+#endif
+
+#ifdef L_lshrsi3
+       .sect .text
+       .globl ___lshrsi3
+
+___lshrsi3:
+       xgdy
+       clra
+       andb    #0x1f
+       xgdy
+       beq     Return
+Loop:
+       xgdx
+       lsrd
+       xgdx
+       rora
+       rorb
+       dey
+       bne     Loop
+Return:
+       rts
+#endif
+
+#ifdef L_lshrhi3
+       .sect .text
+       .globl ___lshrhi3
+
+___lshrhi3:
+       cpx     #16
+       bge     Return_zero
+       cpx     #0
+       beq     Return
+Loop:
+       lsrd
+       dex
+       bne     Loop
+Return:
+       rts
+Return_zero:
+       clra
+       clrb
+       rts
+#endif
+       
+#ifdef L_lshlhi3
+       .sect .text
+       .globl ___lshlhi3
+
+___lshlhi3:
+       cpx     #16
+       bge     Return_zero
+       cpx     #0
+       beq     Return
+Loop:
+       lsld
+       dex
+       bne     Loop
+Return:
+       rts
+Return_zero:
+       clra
+       clrb
+       rts
+#endif
+
+#ifdef L_ashrhi3
+       .sect .text
+       .globl ___ashrhi3
+
+___ashrhi3:
+       cpx     #16
+       bge     Return_minus_1_or_zero
+       cpx     #0
+       beq     Return
+Loop:
+       asra
+       rorb
+       dex
+       bne     Loop
+Return:
+       rts
+Return_minus_1_or_zero:
+       clrb
+       tsta
+       bpl     Return_zero
+       comb
+Return_zero:
+       tba
+       rts
+#endif
+       
+#ifdef L_ashrqi3
+       .sect .text
+       .globl ___ashrqi3
+
+___ashrqi3:
+       cmpa    #8
+       bge     Return_minus_1_or_zero
+       tsta
+       beq     Return
+Loop:
+       asrb
+       deca
+       bne     Loop
+Return:
+       rts
+Return_minus_1_or_zero:
+       clrb
+       tstb
+       bpl     Return_zero
+       coma
+Return_zero:
+       tab
+       rts
+#endif
+
+#ifdef L_lshlqi3
+       .sect .text
+       .globl ___lshlqi3
+
+___lshlqi3:
+       cmpa    #8
+       bge     Return_zero
+       tsta
+       beq     Return
+Loop:
+       lslb
+       deca
+       bne     Loop
+Return:
+       rts
+Return_zero:
+       clrb
+       rts
+#endif
+
+#ifdef L_divmodhi4
+       .sect .text
+       .globl __divmodhi4
+
+;
+;; D = numerator
+;; X = denominator
+;;
+;; Result:     D = D / X
+;;             X = D % X
+;; 
+__divmodhi4:
+       tsta
+       bpl     Numerator_pos
+       comb                    ; D = -D <=> D = (~D) + 1
+       coma
+       xgdx
+       inx
+       tsta
+       bpl     Numerator_neg_denominator_pos
+Numerator_neg_denominator_neg:
+       comb                    ; X = -X
+       coma
+       addd    #1
+       xgdx
+       idiv
+       coma
+       comb
+       xgdx                    ; Remainder <= 0 and result >= 0
+       inx
+       rts
+
+Numerator_pos_denominator_pos:
+       xgdx
+       idiv
+       xgdx                    ; Both values are >= 0
+       rts
+       
+Numerator_pos:
+       xgdx
+       tsta
+       bpl     Numerator_pos_denominator_pos
+Numerator_pos_denominator_neg:
+       coma                    ; X = -X
+       comb
+       xgdx
+       inx
+       idiv
+       xgdx                    ; Remainder >= 0 but result <= 0
+       coma
+       comb
+       addd    #1
+       rts
+       
+Numerator_neg_denominator_pos:
+       xgdx
+       idiv
+       coma                    ; One value is > 0 and the other < 0
+       comb                    ; Change the sign of result and remainder
+       xgdx
+       inx
+       coma
+       comb
+       addd    #1
+       rts
+#endif
+
+#ifdef L_mulqi3
+       .sect .text
+       .globl __mulqi3
+
+;
+; short __mulqi3(signed char a, signed char b);
+;
+;      signed char a   -> register A
+;      signed char b   -> register B
+;
+; returns the signed result of A * B in register D.
+;
+__mulqi3:
+       tsta
+       bmi     A_neg
+       tstb
+       bmi     B_neg
+       mul
+       rts
+B_neg:
+       negb
+       bra     A_or_B_neg
+A_neg:
+       nega
+       tstb
+       bmi     AB_neg
+A_or_B_neg:
+       mul
+       coma
+       comb
+       addd    #1
+       rts
+AB_neg:
+       nega
+       negb
+       mul
+       rts
+#endif
+       
+#ifdef L_mulhi3
+       .sect .text
+       .globl ___mulhi3
+
+;
+;
+;  unsigned short ___mulhi3(unsigned short a, unsigned short b)
+;
+;      a = register D
+;      b = register X
+;
+___mulhi3:
+       stx     *_.tmp
+       pshb
+       ldab    *_.tmp+1
+       mul                     ; A.high * B.low
+       ldaa    *_.tmp
+       stab    *_.tmp
+       pulb
+       pshb
+       mul                     ; A.low * B.high
+       addb    *_.tmp
+       stab    *_.tmp
+       ldaa    *_.tmp+1
+       pulb
+       mul                     ; A.low * B.low
+       adda    *_.tmp
+       rts
+#endif
+
+#ifdef L_mulhi32
+       .sect .text
+       .globl __mulhi32
+
+;
+;
+;  unsigned long __mulhi32(unsigned short a, unsigned short b)
+;
+;      a = register D
+;      b = value on stack
+;
+;      +---------------+
+;       |  B low       | <- 5,x
+;      +---------------+
+;       |  B high      | <- 4,x
+;      +---------------+
+;       |  PC low      |  
+;      +---------------+
+;       |  PC high     |  
+;      +---------------+
+;      |  A low        |
+;      +---------------+
+;      |  A high       |
+;      +---------------+  <- 0,x
+;
+;
+;      <B-low>    5,x
+;      <B-high>   4,x
+;      <ret>      2,x
+;      <A-low>    1,x
+;      <A-high>   0,x
+;
+__mulhi32:
+       pshb
+       psha
+       tsx
+       ldab    4,x
+       mul
+       xgdy                    ; A.high * B.high
+       ldab    5,x
+       pula
+       mul                     ; A.high * B.low
+       std     *_.tmp
+       ldaa    1,x
+       ldab    4,x
+       mul                     ; A.low * B.high
+       addd    *_.tmp
+       stab    *_.tmp
+       tab
+       aby
+       bcc     N
+       ldab    #0xff
+       aby
+       iny
+N:
+       ldab    5,x
+       pula
+       mul                     ; A.low * B.low
+       adda    *_.tmp
+       bcc     Ret
+       iny
+Ret:
+       pshy
+       pulx
+       rts
+       
+#endif
+
+#ifdef L_mulsi3
+       .sect .text
+       .globl __mulsi3
+
+;
+;      <B-low>    8,y
+;      <B-high>   6,y
+;      <ret>      4,y
+;      <tmp>     2,y
+;      <A-low>    0,y
+;
+; D,X   -> A
+; Stack -> B
+;
+; The result is:
+;
+;      (((A.low * B.high) + (A.high * B.low)) << 16) + (A.low * B.low)
+;
+;
+;
+
+B_low  =       8
+B_high =       6
+A_low  =       0
+A_high =       2
+__mulsi3:
+       pshx
+       pshb
+       psha
+       tsy
+;
+; If B.low is 0, optimize into: (A.low * B.high) << 16
+;
+       ldd     B_low,y
+       beq     B_low_zero
+;
+; If A.high is 0, optimize into: (A.low * B.high) << 16 + (A.low * B.low)
+;
+       stx     *_.tmp
+       beq     A_high_zero
+       bsr     ___mulhi3               ; A.high * B.low
+;
+; If A.low is 0, optimize into: (A.high * B.low) << 16
+;
+       ldx     A_low,y
+       beq     A_low_zero              ; X = 0, D = A.high * B.low
+       std     2,y
+;
+; If B.high is 0, we can avoid the (A.low * B.high) << 16 term.
+;
+       ldd     B_high,y
+       beq     B_high_zero
+       bsr     ___mulhi3               ; A.low * B.high
+       addd    2,y
+       std     2,y
+;
+; Here, we know that A.low and B.low are not 0.
+;
+B_high_zero:
+       ldd     B_low,y                 ; A.low is on the stack
+       bsr     __mulhi32               ; A.low * B.low
+       xgdx
+       tsy                             ; Y was clobbered, get it back
+       addd    2,y
+A_low_zero:                            ; See A_low_zero_non_optimized below
+       xgdx
+Return:
+       ins
+       ins
+       ins
+       ins
+       rts
+;
+; 
+; A_low_zero_non_optimized:
+;
+; At this step, X = 0 and D = (A.high * B.low)
+; Optimize into: (A.high * B.low) << 16
+;
+;      xgdx
+;      clra                    ; Since X was 0, clearing D is superfuous.
+;      clrb
+;      bra     Return
+; ----------------
+; B.low == 0, the result is:   (A.low * B.high) << 16
+;
+; At this step:
+;   D = B.low                          = 0 
+;   X = A.high                         ?
+;       A.low is at A_low,y            ?
+;       B.low is at B_low,y            ?
+;
+B_low_zero:
+       ldd     A_low,y
+       beq     Zero1
+       ldx     B_high,y
+       beq     Zero2
+       bsr     ___mulhi3
+Zero1:
+       xgdx
+Zero2:
+       clra
+       clrb
+       bra     Return
+; ----------------
+; A.high is 0, optimize into: (A.low * B.high) << 16 + (A.low * B.low)
+;
+; At this step:
+;   D = B.low                          != 0 
+;   X = A.high                         = 0
+;       A.low is at A_low,y            ?
+;       B.low is at B_low,y            ?
+;
+A_high_zero:
+       ldd     A_low,y         ; A.low
+       beq     Zero1
+       ldx     B_high,y        ; B.high
+       beq     A_low_B_low
+       bsr     ___mulhi3
+       std     2,y
+       bra     B_high_zero     ; Do the (A.low * B.low) and the add.
+
+; ----------------
+; A.high and B.high are 0 optimize into: (A.low * B.low)
+;
+; At this step:
+;   D = B.high                         = 0 
+;   X = A.low                          != 0
+;       A.low is at A_low,y            != 0
+;       B.high is at B_high,y          = 0
+;
+A_low_B_low:
+       ldd     B_low,y                 ; A.low is on the stack
+       bsr     __mulhi32
+       bra     Return
+#endif
+
+#ifdef L_map_data
+
+       .sect   .install3,"ax",@progbits
+       .globl  __map_data_section
+
+__map_data_section:
+       ldd     #__data_section_size
+       beq     Done
+       ldx     #__data_image
+       ldy     #__data_section_start
+Loop:
+       psha
+       ldaa    0,x
+       staa    0,y
+       pula
+       inx
+       iny
+       subd    #1
+       bne     Loop
+Done:
+
+#endif
+
+#ifdef L_init_bss
+
+       .sect   .install3,"ax",@progbits
+       .globl  __init_bss_section
+
+__init_bss_section:
+       ldd     #__bss_size
+       beq     Done
+       ldx     #__bss_start
+Loop:
+       clr     0,x
+       inx
+       subd    #1
+       bne     Loop
+Done:
+
+#endif
+       
+;-----------------------------------------
+; end required gcclib code
+;-----------------------------------------
diff --git a/gcc/config/m68hc11/m68hc11-crt0.S b/gcc/config/m68hc11/m68hc11-crt0.S
new file mode 100644 (file)
index 0000000..2e6a144
--- /dev/null
@@ -0,0 +1,87 @@
+/* Startup code for M68HC11.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+
+This file 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.
+
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file with other programs, and to distribute
+those programs without any restriction coming from the use of this
+file.  (The General Public License restrictions do apply in other
+respects; for example, they cover modification of the file, and
+distribution when not linked into another program.)
+
+This file 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 this program; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, if you link this library with other files,
+   some of which are compiled with GCC, to produce an executable,
+   this library does not by itself cause the resulting executable
+   to be covered by the GNU General Public License.
+   This exception does not however invalidate any other reasons why
+   the executable file might be covered by the GNU General Public License.  */
+       
+;-----------------------------------------
+; startup code
+;-----------------------------------------
+       .file   "crt0.s"
+
+;; 
+;; 
+;; The linker concatenate the .install* sections in the following order:
+;; 
+;; .install0   Setup the stack pointer
+;; .install1   Place holder for applications
+;; .install2   Optional installation of data section in memory
+;; .install3   Place holder for applications
+;; .install4   Invokes the main
+;; 
+       .sect   .install0,"ax",@progbits
+       .globl _start
+
+_start:
+;;
+;; At this step, the stack is not initialized and interrupts are masked.
+;; Applications only have 64 cycles to initialize some registers.
+;;
+;; To have a generic/configurable startup, initialize the stack to
+;; the end of some memory region.  The _stack symbol is defined by
+;; the linker.
+;;
+       lds     #_stack
+       
+       .sect   .install2,"ax",@progbits
+;;
+;; Call a specific initialization operation.  The default is empty.
+;; It can be overriden by applications.  It is intended to initialize
+;; the 68hc11 registers.  Function prototype is:
+;; 
+;;     int __premain(void);
+;; 
+       jsr     __premain
+       
+;;
+;; 
+;;
+       .sect   .install4,"ax",@progbits
+       jsr     main
+fatal:
+       jsr     exit
+       bra fatal
+
+;-----------------------------------------
+; end startup code
+;-----------------------------------------
+;; Force loading of data section mapping and bss clear
+       .2byte  __map_data_section
+       .2byte  __init_bss_section
diff --git a/gcc/config/m68hc11/m68hc11-protos.h b/gcc/config/m68hc11/m68hc11-protos.h
new file mode 100644 (file)
index 0000000..b63fa30
--- /dev/null
@@ -0,0 +1,174 @@
+/* Prototypes for exported functions defined in m68hc11.c
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Contributed by Stephane Carrez (stcarrez@worldnet.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.  */
+
+
+extern int m68hc11_override_options PARAMS((void));
+extern void m68hc11_conditional_register_usage PARAMS((void));
+extern int hard_regno_mode_ok PARAMS((int, enum machine_mode));
+
+extern int m68hc11_total_frame_size PARAMS((void));
+extern int m68hc11_initial_frame_pointer_offset PARAMS((void));
+extern int m68hc11_initial_elimination_offset PARAMS((int, int));
+
+extern void expand_prologue PARAMS((void));
+extern void expand_epilogue PARAMS((void));
+extern void output_function_prologue PARAMS((FILE*, int));
+extern void output_function_epilogue PARAMS((FILE*, int));
+extern int m68hc11_function_block_profiler PARAMS((FILE*,int));
+
+extern int m68hc11_block_profiler PARAMS((FILE*,int));
+
+extern void m68hc11_asm_file_start PARAMS((FILE*, char*));
+
+#ifdef TREE_CODE
+extern void m68hc11_function_arg_advance PARAMS((CUMULATIVE_ARGS*,
+                                                 enum machine_mode,
+                                                 tree,
+                                                 int));
+extern int m68hc11_valid_decl_attribute_p PARAMS((tree, tree,
+                                                 tree, tree));
+extern int m68hc11_valid_type_attribute_p PARAMS((tree, tree,
+                                                 tree, tree));
+extern int m68hc11_comp_type_attributes PARAMS((tree, tree));
+extern void m68hc11_set_default_type_attributes PARAMS((tree));
+extern void m68hc11_encode_section_info PARAMS((tree));
+#endif
+
+#ifdef RTX_CODE
+#if GCC_VERSION > 2095
+extern rtx m68hc11_compare_op0;
+extern rtx m68hc11_compare_op1;
+#endif
+
+extern rtx m68hc11_soft_tmp_reg;
+extern rtx iy_reg;
+extern rtx d_reg;
+
+extern rtx m68hc11_expand_compare_and_branch PARAMS((enum rtx_code,
+                                                     rtx, rtx, rtx));
+extern enum reg_class preferred_reload_class PARAMS((rtx, enum reg_class));
+
+extern int m68hc11_go_if_legitimate_address PARAMS((rtx,
+                                                    enum machine_mode,
+                                                    int));
+
+extern int m68hc11_legitimize_address PARAMS((rtx*, rtx, enum machine_mode));
+
+extern void m68hc11_notice_update_cc PARAMS((rtx, rtx));
+
+extern void m68hc11_reorg PARAMS((rtx));
+
+extern void m68hc11_gen_movqi PARAMS((rtx, rtx*));
+extern void m68hc11_gen_movhi PARAMS((rtx, rtx*));
+extern void m68hc11_gen_rotate PARAMS((enum rtx_code, rtx, rtx*));
+
+extern void m68hc11_output_swap PARAMS((rtx,rtx*));
+
+extern int next_insn_test_reg PARAMS((rtx,rtx));
+
+extern void print_operand PARAMS((FILE*,rtx,int));
+extern void print_operand_address PARAMS((FILE*,rtx));
+
+extern int m68hc11_reload_operands PARAMS((rtx*));
+
+extern int dead_register_here PARAMS((rtx, rtx));
+
+extern int push_pop_operand_p PARAMS((rtx));
+extern void m68hc11_split_move PARAMS((rtx, rtx, rtx));
+extern void m68hc11_split_compare_and_branch PARAMS((enum rtx_code,
+                                                     rtx, rtx, rtx));
+extern void aux_restore_IX_IY PARAMS((rtx));
+extern void aux_validate_IX_IY PARAMS((rtx));
+
+extern rtx m68hc11_gen_lowpart PARAMS((enum machine_mode, rtx));
+extern rtx m68hc11_gen_highpart PARAMS((enum machine_mode, rtx));
+
+#ifdef HAVE_MACHINE_MODES
+extern int m68hc11_memory_move_cost PARAMS((enum machine_mode, enum reg_class,
+                                           int));
+extern int m68hc11_register_move_cost PARAMS((enum reg_class, enum reg_class));
+extern int m68hc11_rtx_costs PARAMS((rtx, enum rtx_code, enum rtx_code));
+extern int m68hc11_address_cost PARAMS((rtx));
+
+
+extern void m68hc11_emit_libcall PARAMS((const char*, enum rtx_code,
+                                         enum machine_mode, enum machine_mode,
+                                         int, rtx*));
+extern int m68hc11_small_indexed_indirect_p PARAMS((rtx, enum machine_mode));
+extern int go_if_legitimate_address2 PARAMS((rtx, enum machine_mode, int));
+
+extern int reg_or_indexed_operand PARAMS((rtx,enum machine_mode));
+extern int tst_operand PARAMS((rtx,enum machine_mode));
+extern int cmp_operand PARAMS((rtx,enum machine_mode));
+extern int memory_indexed_operand PARAMS((rtx, enum machine_mode));
+
+extern void m68hc11_split_logical PARAMS((enum machine_mode, int, rtx*));
+
+extern int m68hc11_register_indirect_p PARAMS((rtx, enum machine_mode));
+
+extern int symbolic_memory_operand PARAMS((rtx, enum machine_mode));
+
+extern int memory_reload_operand PARAMS((rtx, enum machine_mode));
+extern int stack_register_operand PARAMS((rtx, enum machine_mode));
+extern int d_register_operand PARAMS((rtx, enum machine_mode));
+extern int hard_addr_reg_operand PARAMS((rtx, enum machine_mode));
+extern int arith_src_operand PARAMS((rtx, enum machine_mode));
+extern int m68hc11_logical_operator PARAMS((rtx, enum machine_mode));
+extern int m68hc11_arith_operator PARAMS((rtx, enum machine_mode));
+extern int m68hc11_non_shift_operator PARAMS((rtx, enum machine_mode));
+extern int m68hc11_unary_operator PARAMS((rtx, enum machine_mode));
+extern int non_push_operand PARAMS((rtx, enum machine_mode));
+extern int hard_reg_operand PARAMS((rtx, enum machine_mode));
+extern int soft_reg_operand PARAMS((rtx, enum machine_mode));
+extern int reg_or_some_mem_operand PARAMS((rtx, enum machine_mode));
+
+extern enum reg_class limit_reload_class PARAMS((enum machine_mode, enum reg_class));
+
+#if defined TREE_CODE
+extern void m68hc11_init_cumulative_args PARAMS((CUMULATIVE_ARGS*,
+                                                 tree,
+                                                 rtx));
+
+extern rtx m68hc11_function_arg PARAMS((const CUMULATIVE_ARGS* ,
+                                        enum machine_mode,
+                                        tree, int));
+extern int m68hc11_function_arg_pass_by_reference PARAMS((const CUMULATIVE_ARGS*,
+                                                          enum machine_mode,
+                                                          tree,
+                                                          int));
+extern int m68hc11_function_arg_padding PARAMS((enum machine_mode, tree));
+
+extern void m68hc11_expand_builtin_va_start PARAMS((int, tree, rtx));
+
+extern rtx m68hc11_va_arg PARAMS((tree,tree));
+extern void m68hc11_expand_builtin_va_start PARAMS((int,tree,rtx));
+
+extern void m68hc11_function_epilogue PARAMS((FILE*,int));
+
+#endif /* TREE_CODE */
+
+#if GCC_VERSION > 2095
+extern HOST_WIDE_INT m68hc11_min_offset;
+extern HOST_WIDE_INT m68hc11_max_offset;
+#endif
+#endif /* HAVE_MACHINE_MODES */
+#endif /* RTX_CODE */
+
diff --git a/gcc/config/m68hc11/m68hc11.c b/gcc/config/m68hc11/m68hc11.c
new file mode 100644 (file)
index 0000000..787ddd1
--- /dev/null
@@ -0,0 +1,5067 @@
+/* Subroutines for code generation on Motorola 68HC11 and 68HC12.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Contributed by Stephane Carrez (stcarrez@worldnet.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
+
+*/
+
+#include <stdio.h>
+#include "config.h"
+#include "system.h"
+#include "rtl.h"
+#include "tree.h"
+#if GCC_VERSION > 2095
+#include "tm_p.h"
+#endif
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "recog.h"
+#include "expr.h"
+#include "toplev.h"
+#include "basic-block.h"
+#if GCC_VERSION > 2095
+#include "function.h"
+#include "ggc.h"
+#else
+#include "m68hc11-protos.h"
+#endif
+
+#if GCC_VERSION == 2095
+extern char *version_string;
+#endif
+
+static void print_options PARAMS ((FILE *));
+static void emit_move_after_reload PARAMS ((rtx, rtx, rtx));
+static rtx simplify_logical PARAMS ((enum machine_mode, int, rtx, rtx *));
+static void m68hc11_emit_logical PARAMS ((enum machine_mode, int, rtx *));
+static int go_if_legitimate_address_internal PARAMS((rtx, enum machine_mode,
+                                                     int));
+static int register_indirect_p PARAMS((rtx, enum machine_mode, int));
+static rtx m68hc11_expand_compare PARAMS((enum rtx_code, rtx, rtx));
+static int must_parenthesize PARAMS ((rtx));
+
+static int m68hc11_auto_inc_p PARAMS ((rtx));
+
+void create_regs_rtx PARAMS ((void));
+
+static void asm_print_register PARAMS ((FILE *, int));
+
+rtx m68hc11_soft_tmp_reg;
+
+/* Must be set to 1 to produce debug messages. */
+int debug_m6811 = 0;
+
+extern FILE *asm_out_file;
+
+rtx ix_reg;
+rtx iy_reg;
+rtx d_reg;
+rtx da_reg;
+rtx stack_push_word;
+rtx stack_pop_word;
+static int regs_inited = 0;
+static rtx z_reg;
+
+/* Set to 1 by expand_prologue() when the function is an interrupt handler.  */
+int current_function_interrupt;
+
+/* Set to 1 by expand_prologue() when the function is a trap handler.  */
+int current_function_trap;
+
+/* Min offset that is valid for the indirect addressing mode.  */
+HOST_WIDE_INT m68hc11_min_offset = 0;
+
+/* Max offset that is valid for the indirect addressing mode.  */
+HOST_WIDE_INT m68hc11_max_offset = 256;
+
+/* The class value for base registers.  */
+enum reg_class m68hc11_base_reg_class = A_REGS;
+
+/* The class value for index registers.  This is NO_REGS for 68HC11.  */
+enum reg_class m68hc11_index_reg_class = NO_REGS;
+
+enum reg_class m68hc11_tmp_regs_class = NO_REGS;
+
+/* Tables that tell whether a given hard register is valid for
+   a base or an index register.  It is filled at init time depending
+   on the target processor.  */
+unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
+unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
+
+/* A correction offset which is applied to the stack pointer.
+   This is 1 for 68HC11 and 0 for 68HC12.  */
+int m68hc11_sp_correction;
+
+/* Comparison operands saved by the "tstxx" and "cmpxx" expand patterns.  */
+rtx m68hc11_compare_op0;
+rtx m68hc11_compare_op1;
+\f
+
+/* Machine specific options */
+
+const char *m68hc11_regparm_string;
+const char *m68hc11_reg_alloc_order;
+const char *m68hc11_soft_reg_count;
+
+static void m68hc11_add_gc_roots PARAMS ((void));
+
+static int nb_soft_regs;
+
+#if GCC_VERSION > 2095
+/* Flag defined in c-decl.c
+
+   Nonzero means don't recognize the non-ANSI builtin functions.
+   -ansi sets this.
+
+   It is set by 'm68hc11_override_options' to ensure that bcmp() and
+   bzero() are not defined.  Their prototype are wrong and they
+   conflict with newlib definition.  Don't define as external to
+   avoid a link problem for f77.  */
+int flag_no_nonansi_builtin;
+#endif
+
+int
+m68hc11_override_options ()
+{
+  m68hc11_add_gc_roots ();
+
+#if GCC_VERSION > 2095
+  flag_no_nonansi_builtin = 1;
+#endif
+  
+  memset (m68hc11_reg_valid_for_index, 0,
+         sizeof (m68hc11_reg_valid_for_index));
+  memset (m68hc11_reg_valid_for_base, 0, sizeof (m68hc11_reg_valid_for_base));
+
+  /* Configure for a 68hc11 processor.  */
+  if (TARGET_M6811)
+    {
+      /* If gcc was built for a 68hc12, invalidate that because
+         a -m68hc11 option was specified on the command line.  */
+      if (TARGET_DEFAULT != MASK_M6811)
+        target_flags &= ~TARGET_DEFAULT;
+      
+      m68hc11_min_offset = 0;
+      m68hc11_max_offset = 256;
+      m68hc11_index_reg_class = NO_REGS;
+      m68hc11_base_reg_class = A_REGS;
+      m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
+      m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
+      m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
+      m68hc11_sp_correction = 1;
+      m68hc11_tmp_regs_class = D_REGS;
+      if (m68hc11_soft_reg_count == 0 && !TARGET_M6812)
+       m68hc11_soft_reg_count = "4";
+    }
+
+  /* Configure for a 68hc12 processor.  */
+  if (TARGET_M6812)
+    {
+      m68hc11_min_offset = 0;
+      m68hc11_max_offset = 65536;
+      m68hc11_index_reg_class = D_REGS;
+      m68hc11_base_reg_class = A_OR_SP_REGS;
+      m68hc11_reg_valid_for_base[HARD_X_REGNUM] = 1;
+      m68hc11_reg_valid_for_base[HARD_Y_REGNUM] = 1;
+      m68hc11_reg_valid_for_base[HARD_Z_REGNUM] = 1;
+      m68hc11_reg_valid_for_base[HARD_SP_REGNUM] = 1;
+      m68hc11_reg_valid_for_index[HARD_D_REGNUM] = 1;
+      m68hc11_sp_correction = 0;
+      m68hc11_tmp_regs_class = TMP_REGS;
+      target_flags &= ~MASK_M6811;
+      if (m68hc11_soft_reg_count == 0)
+       m68hc11_soft_reg_count = "2";
+    }
+  return 0;
+}
+
+
+void
+m68hc11_conditional_register_usage ()
+{
+  int i;
+  int cnt = atoi (m68hc11_soft_reg_count);
+
+  if (cnt < 0)
+    cnt = 0;
+  if (cnt > SOFT_REG_LAST - SOFT_REG_FIRST)
+    cnt = SOFT_REG_LAST - SOFT_REG_FIRST;
+
+  nb_soft_regs = cnt;
+  for (i = SOFT_REG_FIRST + cnt; i < SOFT_REG_LAST; i++)
+    {
+      fixed_regs[i] = 1;
+      call_used_regs[i] = 1;
+    }
+}
+\f
+
+/* Reload and register operations. */
+
+static const char *reg_class_names[] = REG_CLASS_NAMES;
+
+
+void
+create_regs_rtx ()
+{
+  /*  regs_inited = 1; */
+  ix_reg = gen_rtx (REG, HImode, HARD_X_REGNUM);
+  iy_reg = gen_rtx (REG, HImode, HARD_Y_REGNUM);
+  d_reg = gen_rtx (REG, HImode, HARD_D_REGNUM);
+  da_reg = gen_rtx (REG, QImode, HARD_A_REGNUM);
+  m68hc11_soft_tmp_reg = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
+
+  stack_push_word = gen_rtx (MEM, HImode,
+                            gen_rtx (PRE_DEC, HImode,
+                                     gen_rtx (REG, HImode, HARD_SP_REGNUM)));
+  stack_pop_word = gen_rtx (MEM, HImode,
+                           gen_rtx (POST_INC, HImode,
+                                    gen_rtx (REG, HImode, HARD_SP_REGNUM)));
+
+}
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
+    - 8 bit values are stored anywhere (except the SP register).
+    - 16 bit values can be stored in any register whose mode is 16
+    - 32 bit values can be stored in D, X registers or in a soft register
+      (except the last one because we need 2 soft registers)
+    - Values whose size is > 32 bit are not stored in real hard
+      registers.  They may be stored in soft registers if there are
+      enough of them.  */
+int
+hard_regno_mode_ok (regno, mode)
+     int regno;
+     enum machine_mode mode;
+{
+  switch (GET_MODE_SIZE (mode))
+    {
+    case 8:
+      return S_REGNO_P (regno) && nb_soft_regs >= 4;
+
+    case 4:
+      return X_REGNO_P (regno) || (S_REGNO_P (regno) && nb_soft_regs >= 2);
+
+    case 2:
+      return G_REGNO_P (regno);
+
+    case 1:
+      /* We have to accept a QImode in X or Y registers.  Otherwise, the
+         reload pass will fail when some (SUBREG:QI (REG:HI X)) are defined
+         in the insns.  Reload fails if the insn rejects the register class 'a'
+         as well as if it accepts it.  Patterns that failed were
+         zero_extend_qihi2 and iorqi3.  */
+
+      return G_REGNO_P (regno) && !SP_REGNO_P (regno);
+
+    default:
+      return 0;
+    }
+}
+
+enum reg_class
+limit_reload_class (mode, class)
+     enum machine_mode mode;
+     enum reg_class class;
+{
+  if (mode == Pmode)
+    {
+      if (class == m68hc11_base_reg_class || class == SP_REGS
+         || class == Y_REGS || class == X_REGS
+         || class == X_OR_SP_REGS || class == Y_OR_S_REGS
+         || class == A_OR_SP_REGS)
+       return class;
+
+      if (debug_m6811)
+       {
+         printf ("Forcing to A_REGS\n");
+         fflush (stdout);
+       }
+      return m68hc11_base_reg_class;
+    }
+  return class;
+}
+
+enum reg_class
+preferred_reload_class (operand, class)
+     rtx operand;
+     enum reg_class class;
+{
+  enum machine_mode mode;
+
+  mode = GET_MODE (operand);
+
+  if (debug_m6811)
+    {
+      printf ("Preferred reload: (class=%s): ", reg_class_names[class]);
+    }
+
+  if (class == D_OR_A_OR_S_REGS && SP_REG_P (operand))
+    return m68hc11_base_reg_class;
+
+  if (class >= S_REGS && (GET_CODE (operand) == MEM
+                         || GET_CODE (operand) == CONST_INT))
+    {
+      /* S_REGS class must not be used.  The movhi template does not
+         work to move a memory to a soft register.
+         Restrict to a hard reg.  */
+      switch (class)
+       {
+       default:
+       case G_REGS:
+       case D_OR_A_OR_S_REGS:
+         class = A_OR_D_REGS;
+         break;
+       case A_OR_S_REGS:
+         class = A_REGS;
+         break;
+       case D_OR_SP_OR_S_REGS:
+         class = D_OR_SP_REGS;
+         break;
+       case D_OR_Y_OR_S_REGS:
+         class = D_OR_Y_REGS;
+         break;
+       case D_OR_X_OR_S_REGS:
+         class = D_OR_X_REGS;
+         break;
+       case SP_OR_S_REGS:
+         class = SP_REGS;
+         break;
+       case Y_OR_S_REGS:
+         class = Y_REGS;
+         break;
+       case X_OR_S_REGS:
+         class = X_REGS;
+         break;
+       case D_OR_S_REGS:
+         class = D_REGS;
+       }
+    }
+  else if (class == Y_REGS && GET_CODE (operand) == MEM)
+    {
+      class = Y_REGS;
+    }
+  else if (class == A_OR_D_REGS && GET_MODE_SIZE (mode) == 4)
+    {
+      class = D_OR_X_REGS;
+    }
+  else if (class >= S_REGS && S_REG_P (operand))
+    {
+      switch (class)
+       {
+       default:
+       case G_REGS:
+       case D_OR_A_OR_S_REGS:
+         class = A_OR_D_REGS;
+         break;
+       case A_OR_S_REGS:
+         class = A_REGS;
+         break;
+       case D_OR_SP_OR_S_REGS:
+         class = D_OR_SP_REGS;
+         break;
+       case D_OR_Y_OR_S_REGS:
+         class = D_OR_Y_REGS;
+         break;
+       case D_OR_X_OR_S_REGS:
+         class = D_OR_X_REGS;
+         break;
+       case SP_OR_S_REGS:
+         class = SP_REGS;
+         break;
+       case Y_OR_S_REGS:
+         class = Y_REGS;
+         break;
+       case X_OR_S_REGS:
+         class = X_REGS;
+         break;
+       case D_OR_S_REGS:
+         class = D_REGS;
+       }
+    }
+  else if (class >= S_REGS)
+    {
+      if (debug_m6811)
+       {
+         printf ("Class = %s for: ", reg_class_names[class]);
+         fflush (stdout);
+         debug_rtx (operand);
+       }
+    }
+
+  if (debug_m6811)
+    {
+      printf (" => class=%s\n", reg_class_names[class]);
+      fflush (stdout);
+      debug_rtx (operand);
+    }
+
+  return class;
+}
+
+/* Return 1 if the operand is a valid indexed addressing mode.
+   For 68hc11:  n,r    with n in [0..255] and r in A_REGS class
+   For 68hc12:  n,r    no constraint on the constant, r in A_REGS class.  */
+static int
+register_indirect_p (operand, mode, strict)
+     rtx operand;
+     enum machine_mode mode;
+     int strict;
+{
+  rtx base, offset;
+
+  switch (GET_CODE (operand))
+    {
+    case POST_INC:
+    case PRE_INC:
+    case POST_DEC:
+    case PRE_DEC:
+      if (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+       return register_indirect_p (XEXP (operand, 0), mode, strict);
+      return 0;
+
+    case PLUS:
+      base = XEXP (operand, 0);
+      if (GET_CODE (base) == MEM)
+       return 0;
+
+      offset = XEXP (operand, 1);
+      if (GET_CODE (offset) == MEM)
+       return 0;
+
+      if (GET_CODE (base) == REG)
+       {
+         if (!VALID_CONSTANT_OFFSET_P (offset, mode))
+           return 0;
+
+         if (strict == 0)
+           return 1;
+
+         return REGNO_OK_FOR_BASE_P2 (REGNO (base), strict);
+       }
+      if (GET_CODE (offset) == REG)
+       {
+         if (!VALID_CONSTANT_OFFSET_P (base, mode))
+           return 0;
+
+         if (strict == 0)
+           return 1;
+
+         return REGNO_OK_FOR_BASE_P2 (REGNO (offset), strict);
+       }
+      return 0;
+
+    case REG:
+      return REGNO_OK_FOR_BASE_P2 (REGNO (operand), strict);
+
+    default:
+      return 0;
+    }
+}
+
+/* Returns 1 if the operand fits in a 68HC11 indirect mode or in
+   a 68HC12 1-byte index addressing mode.  */
+int
+m68hc11_small_indexed_indirect_p (operand, mode)
+     rtx operand;
+     enum machine_mode mode;
+{
+  rtx base, offset;
+
+  if (GET_CODE (operand) != MEM)
+    return 0;
+
+  operand = XEXP (operand, 0);
+  if (CONSTANT_ADDRESS_P (operand))
+    return 1;
+
+  if (PUSH_POP_ADDRESS_P (operand))
+    return 1;
+
+  if (!register_indirect_p (operand, mode,
+                            (reload_completed | reload_in_progress)))
+    return 0;
+
+  if (TARGET_M6812 && GET_CODE (operand) == PLUS
+      && (reload_completed | reload_in_progress))
+    {
+      base = XEXP (operand, 0);
+      offset = XEXP (operand, 1);
+      if (GET_CODE (base) == CONST_INT)
+       offset = base;
+
+      switch (GET_MODE_SIZE (mode))
+       {
+       case 8:
+         if (INTVAL (offset) < -16 + 6 || INTVAL (offset) > 15 - 6)
+           return 0;
+         break;
+
+       case 4:
+         if (INTVAL (offset) < -16 + 2 || INTVAL (offset) > 15 - 2)
+           return 0;
+         break;
+
+       default:
+         if (INTVAL (offset) < -16 || INTVAL (offset) > 15)
+           return 0;
+         break;
+       }
+    }
+  return 1;
+}
+
+int
+m68hc11_register_indirect_p (operand, mode)
+     rtx operand;
+     enum machine_mode mode;
+{
+  if (GET_CODE (operand) != MEM)
+    return 0;
+
+  operand = XEXP (operand, 0);
+  return register_indirect_p (operand, mode,
+                              (reload_completed | reload_in_progress));
+}
+
+static int
+go_if_legitimate_address_internal (operand, mode, strict)
+     rtx operand;
+     enum machine_mode mode;
+     int strict;
+{
+  if (CONSTANT_ADDRESS_P (operand))
+    {
+      /* Reject the global variables if they are too wide.  This forces
+         a load of their address in a register and generates smaller code.  */
+      if (GET_MODE_SIZE (mode) == 8)
+       return 0;
+
+      return 1;
+    }
+  if (register_indirect_p (operand, mode, strict))
+    {
+      return 1;
+    }
+  if (PUSH_POP_ADDRESS_P (operand))
+    {
+      return 1;
+    }
+  if (symbolic_memory_operand (operand, mode))
+    {
+      return 1;
+    }
+  return 0;
+}
+
+int
+m68hc11_go_if_legitimate_address (operand, mode, strict)
+     rtx operand;
+     enum machine_mode mode;
+     int strict;
+{
+  int result;
+
+  if (debug_m6811)
+    {
+      printf ("Checking: ");
+      fflush (stdout);
+      debug_rtx (operand);
+    }
+
+  result = go_if_legitimate_address_internal (operand, mode, strict);
+
+  if (debug_m6811)
+    {
+      printf (" -> %s\n", result == 0 ? "NO" : "YES");
+    }
+
+  if (result == 0)
+    {
+      if (debug_m6811)
+       {
+         printf ("go_if_legitimate%s, ret 0: %d:",
+                 (strict ? "_strict" : ""), mode);
+         fflush (stdout);
+         debug_rtx (operand);
+       }
+    }
+  return result;
+}
+
+int
+m68hc11_legitimize_address (operand, old_operand, mode)
+     rtx *operand ATTRIBUTE_UNUSED;
+     rtx old_operand ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return 0;
+}
+
+
+int
+m68hc11_reload_operands (operands)
+     rtx operands[];
+{
+  enum machine_mode mode;
+
+  if (regs_inited == 0)
+    create_regs_rtx ();
+
+  mode = GET_MODE (operands[1]);
+
+  /* Input reload of indirect addressing (MEM (PLUS (REG) (CONST))). */
+  if (A_REG_P (operands[0]) && memory_reload_operand (operands[1], mode))
+    {
+      rtx big_offset = XEXP (XEXP (operands[1], 0), 1);
+      rtx base = XEXP (XEXP (operands[1], 0), 0);
+
+      if (GET_CODE (base) != REG)
+       {
+         rtx tmp = base;
+         base = big_offset;
+         big_offset = tmp;
+       }
+
+      /* If the offset is out of range, we have to compute the address
+         with a separate add instruction.  We try to do with with an 8-bit
+         add on the A register.  This is possible only if the lowest part
+         of the offset (ie, big_offset % 256) is a valid constant offset
+         with respect to the mode.  If it's not, we have to generate a
+         16-bit add on the D register.  From:
+       
+         (SET (REG X (MEM (PLUS (REG X) (CONST_INT 1000)))))
+       
+         we generate:
+        
+         [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
+         (SET (REG A) (PLUS (REG A) (CONST_INT 1000 / 256)))
+         [(SET (REG D) (REG X)) (SET (REG X) (REG D))]
+         (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))
+       
+         (SET (REG X) (PLUS (REG X) (CONST_INT 1000 / 256 * 256)))
+         (SET (REG X) (MEM (PLUS (REG X) (CONST_INT 1000 % 256)))) 
+
+      */
+      if (!VALID_CONSTANT_OFFSET_P (big_offset, mode))
+       {
+         int vh, vl;
+         rtx reg = operands[0];
+         rtx offset;
+         int val = INTVAL (big_offset);
+
+
+         /* We use the 'operands[0]' as a scratch register to compute the
+            address. Make sure 'base' is in that register.  */
+         if (!rtx_equal_p (base, operands[0]))
+           {
+             emit_move_insn (reg, base);
+           }
+
+         if (val > 0)
+           {
+             vh = val >> 8;
+             vl = val & 0x0FF;
+           }
+         else
+           {
+             vh = (val >> 8) & 0x0FF;
+             vl = val & 0x0FF;
+           }
+
+         /* Create the lowest part offset that still remains to be added.
+            If it's not a valid offset, do a 16-bit add.  */
+         offset = gen_rtx (CONST_INT, VOIDmode, vl);
+         if (!VALID_CONSTANT_OFFSET_P (offset, mode))
+           {
+             emit_insn (gen_rtx (SET, VOIDmode, reg,
+                                 gen_rtx (PLUS, HImode, reg, big_offset)));
+             offset = const0_rtx;
+           }
+         else
+           {
+             emit_insn (gen_rtx (SET, VOIDmode, reg,
+                                 gen_rtx (PLUS, HImode, reg,
+                                          gen_rtx (CONST_INT,
+                                                   VOIDmode, vh << 8))));
+           }
+         emit_move_insn (operands[0],
+                         gen_rtx (MEM, GET_MODE (operands[1]),
+                                  gen_rtx (PLUS, Pmode, reg, offset)));
+         return 1;
+       }
+    }
+
+  /* Use the normal gen_movhi pattern. */
+  return 0;
+}
+
+void
+m68hc11_emit_libcall (name, code, dmode, smode, noperands, operands)
+     const char *name;
+     enum rtx_code code;
+     enum machine_mode dmode;
+     enum machine_mode smode;
+     int noperands;
+     rtx *operands;
+{
+  rtx ret;
+  rtx insns;
+  rtx libcall;
+  rtx equiv;
+
+  start_sequence ();
+  libcall = gen_rtx_SYMBOL_REF (Pmode, name);
+  switch (noperands)
+    {
+    case 2:
+      ret = emit_library_call_value (libcall, NULL_RTX, 1, dmode, 1,
+                                     operands[1], smode);
+      equiv = gen_rtx (code, dmode, operands[1]);
+      break;
+
+    case 3:
+      ret = emit_library_call_value (libcall, operands[0], 1, dmode, 2,
+                                     operands[1], smode, operands[2],
+                                     smode);
+      equiv = gen_rtx (code, dmode, operands[1], operands[2]);
+      break;
+
+    default:
+      fatal ("m68hc11_emit_libcall: Bad number of operands");
+    }
+
+  insns = get_insns ();
+  end_sequence ();
+  emit_libcall_block (insns, operands[0], ret, equiv);
+}
+
+/* Returns true if X is a PRE/POST increment decrement
+   (same as auto_inc_p() in rtlanal.c but do not take into
+   account the stack).  */
+static int
+m68hc11_auto_inc_p (x)
+     rtx x;
+{
+  return GET_CODE (x) == PRE_DEC
+    || GET_CODE (x) == POST_INC
+    || GET_CODE (x) == POST_DEC || GET_CODE (x) == PRE_INC;
+}
+\f
+
+/* Predicates for machine description.  */
+
+int
+memory_reload_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return GET_CODE (operand) == MEM
+    && GET_CODE (XEXP (operand, 0)) == PLUS
+    && ((GET_CODE (XEXP (XEXP (operand, 0), 0)) == REG
+        && GET_CODE (XEXP (XEXP (operand, 0), 1)) == CONST_INT)
+       || (GET_CODE (XEXP (XEXP (operand, 0), 1)) == REG
+           && GET_CODE (XEXP (XEXP (operand, 0), 0)) == CONST_INT));
+}
+
+int
+tst_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode;
+{
+  if (GET_CODE (operand) == MEM)
+    {
+      rtx addr = XEXP (operand, 0);
+      if (m68hc11_auto_inc_p (addr))
+       return 0;
+    }
+  return nonimmediate_operand (operand, mode);
+}
+
+int
+cmp_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode;
+{
+  if (GET_CODE (operand) == MEM)
+    {
+      rtx addr = XEXP (operand, 0);
+      if (m68hc11_auto_inc_p (addr))
+       return 0;
+    }
+  return general_operand (operand, mode);
+}
+
+int
+non_push_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode;
+{
+  if (general_operand (operand, mode) == 0)
+    return 0;
+
+  if (push_operand (operand, mode) == 1)
+    return 0;
+  return 1;
+}
+
+int
+reg_or_some_mem_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode;
+{
+  if (GET_CODE (operand) == MEM)
+    {
+      rtx op = XEXP (operand, 0);
+
+      if (symbolic_memory_operand (op, mode))
+       return 1;
+
+      if (IS_STACK_PUSH (operand))
+       return 1;
+
+      if (m68hc11_register_indirect_p (operand, mode))
+       return 1;
+
+      return 0;
+    }
+
+  return register_operand (operand, mode);
+}
+
+int
+stack_register_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return SP_REG_P (operand);
+}
+
+int
+d_register_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  if (GET_CODE (operand) == SUBREG)
+    operand = XEXP (operand, 0);
+
+  return GET_CODE (operand) == REG
+    && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
+       || REGNO (operand) == HARD_D_REGNUM);
+}
+
+int
+hard_addr_reg_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  if (GET_CODE (operand) == SUBREG)
+    operand = XEXP (operand, 0);
+
+  return GET_CODE (operand) == REG
+    && (REGNO (operand) == HARD_X_REGNUM
+       || REGNO (operand) == HARD_Y_REGNUM
+       || REGNO (operand) == HARD_Z_REGNUM);
+}
+
+int
+hard_reg_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  if (GET_CODE (operand) == SUBREG)
+    operand = XEXP (operand, 0);
+
+  return GET_CODE (operand) == REG
+    && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
+       || H_REGNO_P (REGNO (operand)));
+}
+
+int
+memory_indexed_operand (operand, mode)
+     rtx operand;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  if (GET_CODE (operand) != MEM)
+    return 0;
+
+  operand = XEXP (operand, 0);
+  if (GET_CODE (operand) == PLUS)
+    {
+      if (GET_CODE (XEXP (operand, 0)) == REG)
+       operand = XEXP (operand, 0);
+      else if (GET_CODE (XEXP (operand, 1)) == REG)
+       operand = XEXP (operand, 1);
+    }
+  return GET_CODE (operand) == REG
+    && (REGNO (operand) >= FIRST_PSEUDO_REGISTER
+       || A_REGNO_P (REGNO (operand)));
+}
+
+int
+push_pop_operand_p (operand)
+     rtx operand;
+{
+  if (GET_CODE (operand) != MEM)
+    {
+      return 0;
+    }
+  operand = XEXP (operand, 0);
+  return PUSH_POP_ADDRESS_P (operand);
+}
+
+/* Returns 1 if OP is either a symbol reference or a sum of a symbol
+   reference and a constant.  */
+
+int
+symbolic_memory_operand (op, mode)
+     register rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return 1;
+
+    case CONST:
+      return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+              || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+             && GET_CODE (XEXP (op, 1)) == CONST_INT);
+
+      /* ??? This clause seems to be irrelevant.  */
+    case CONST_DOUBLE:
+      return GET_MODE (op) == mode;
+
+    case PLUS:
+      return symbolic_memory_operand (XEXP (op, 0), mode)
+       && symbolic_memory_operand (XEXP (op, 1), mode);
+
+    default:
+      return 0;
+    }
+}
+
+int
+m68hc11_logical_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR;
+}
+
+int
+m68hc11_arith_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
+    || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS
+    || GET_CODE (op) == ASHIFT || GET_CODE (op) == ASHIFTRT
+    || GET_CODE (op) == LSHIFTRT || GET_CODE (op) == ROTATE
+    || GET_CODE (op) == ROTATERT;
+}
+
+int
+m68hc11_non_shift_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return GET_CODE (op) == AND || GET_CODE (op) == IOR || GET_CODE (op) == XOR
+    || GET_CODE (op) == PLUS || GET_CODE (op) == MINUS;
+}
+
+
+int
+m68hc11_unary_operator (op, mode)
+     register rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return GET_CODE (op) == NEG || GET_CODE (op) == NOT
+    || GET_CODE (op) == SIGN_EXTEND || GET_CODE (op) == ZERO_EXTEND;
+}
+\f
+
+/* Profiling.  */
+
+int
+m68hc11_block_profiler (out, blockno)
+     FILE *out ATTRIBUTE_UNUSED;
+     int blockno ATTRIBUTE_UNUSED;
+{
+  return 0;
+}
+
+int
+m68hc11_function_block_profiler (out, block_or_label)
+     FILE *out ATTRIBUTE_UNUSED;
+     int block_or_label ATTRIBUTE_UNUSED;
+{
+  return 0;
+}
+\f
+/* Declaration of types.  */
+
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+   with arguments ARGS is a valid machine specific attribute for DECL.
+   The attributes in ATTRIBUTES have previously been assigned to DECL.  */
+
+int
+m68hc11_valid_decl_attribute_p (decl, attributes, identifier, args)
+     tree decl ATTRIBUTE_UNUSED;
+     tree attributes ATTRIBUTE_UNUSED;
+     tree identifier ATTRIBUTE_UNUSED;
+     tree args ATTRIBUTE_UNUSED;
+{
+  return 0;
+}
+
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+   with arguments ARGS is a valid machine specific attribute for TYPE.
+   The attributes in ATTRIBUTES have previously been assigned to TYPE.  */
+
+int
+m68hc11_valid_type_attribute_p (type, attributes, identifier, args)
+     tree type;
+     tree attributes ATTRIBUTE_UNUSED;
+     tree identifier;
+     tree args;
+{
+  if (TREE_CODE (type) != FUNCTION_TYPE
+      && TREE_CODE (type) != FIELD_DECL && TREE_CODE (type) != TYPE_DECL)
+    return 0;
+
+  if (TREE_CODE (type) == FUNCTION_TYPE)
+    {
+      if (is_attribute_p ("interrupt", identifier))
+       return (args == NULL_TREE);
+      if (is_attribute_p ("trap", identifier))
+       return (args == NULL_TREE);
+    }
+
+  return 0;
+}
+
+/* If defined, a C expression whose value is zero if the attributes on
+   TYPE1 and TYPE2 are incompatible, one if they are compatible, and
+   two if they are nearly compatible (which causes a warning to be
+   generated).  */
+
+int
+m68hc11_comp_type_attributes (type1, type2)
+     tree type1 ATTRIBUTE_UNUSED;
+     tree type2 ATTRIBUTE_UNUSED;
+{
+  return 1;
+}
+
+/* If defined, a C statement that assigns default attributes to newly
+   defined TYPE.  */
+
+void
+m68hc11_set_default_type_attributes (type)
+     tree type ATTRIBUTE_UNUSED;
+{
+}
+
+/* Define this macro if references to a symbol must be treated
+   differently depending on something about the variable or function
+   named by the symbol (such as what section it is in).
+
+   For the 68HC11, we want to recognize trap handlers so that we
+   handle calls to traps in a special manner (by issuing the trap).
+   This information is stored in SYMBOL_REF_FLAG.  */
+void
+m68hc11_encode_section_info (decl)
+     tree decl;
+{
+  tree func_attr;
+  int trap_handler;
+  rtx rtl;
+
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    return;
+
+  rtl = DECL_RTL (decl);
+
+  func_attr = TYPE_ATTRIBUTES (TREE_TYPE (decl));
+  trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
+  SYMBOL_REF_FLAG (XEXP (rtl, 0)) = trap_handler;
+}
+\f
+
+/* Argument support functions.  */
+
+/* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
+   Arrays are passed by references and other types by value.
+
+   SCz: I tried to pass DImode by reference but it seems that this
+   does not work very well.  */
+int
+m68hc11_function_arg_pass_by_reference (cum, mode, type, named)
+     const CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     tree type;
+     int named ATTRIBUTE_UNUSED;
+{
+  return ((type && TREE_CODE (type) == ARRAY_TYPE)
+         /* Consider complex values as aggregates, so care for TCmode. */
+         /*|| GET_MODE_SIZE (mode) > 4 SCz, temporary */
+         /*|| (type && AGGREGATE_TYPE_P (type))) */ );
+}
+
+
+/* Define the offset between two registers, one to be eliminated, and the
+   other its replacement, at the start of a routine.  */
+int
+m68hc11_initial_elimination_offset (from, to)
+     int from;
+     int to;
+{
+  int trap_handler;
+  tree func_attr;
+  int size;
+  int regno;
+
+  /* For a trap handler, we must take into account the registers which
+     are pushed on the stack during the trap (except the PC).  */
+  func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+  trap_handler = lookup_attribute ("trap", func_attr) != NULL_TREE;
+  if (trap_handler && from == ARG_POINTER_REGNUM)
+    size = 7;
+  else
+    size = 0;
+
+  if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+    {
+      /* 2 is for the saved frame.
+         1 is for the 'sts' correction when creating the frame.  */
+      return get_frame_size () + 2 + m68hc11_sp_correction + size;
+    }
+
+  if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+    {
+      return 0;
+    }
+
+  /* Push any 2 byte pseudo hard registers that we need to save.  */
+  for (regno = SOFT_REG_FIRST; regno < SOFT_REG_LAST; regno++)
+    {
+      if (regs_ever_live[regno] && !call_used_regs[regno])
+       {
+         size += 2;
+       }
+    }
+
+  if (from == ARG_POINTER_REGNUM && to == HARD_SP_REGNUM)
+    {
+      return get_frame_size () + size;
+    }
+
+  if (from == FRAME_POINTER_REGNUM && to == HARD_SP_REGNUM)
+    {
+      return size - m68hc11_sp_correction;
+    }
+  return 0;
+}
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.  */
+
+void
+m68hc11_init_cumulative_args (cum, fntype, libname)
+     CUMULATIVE_ARGS *cum;
+     tree fntype;
+     rtx libname;
+{
+  tree ret_type;
+
+  z_replacement_completed = 0;
+  cum->words = 0;
+  cum->nregs = 0;
+
+  /* For a library call, we must find out the type of the return value.
+     When the return value is bigger than 4 bytes, it is returned in
+     memory.  In that case, the first argument of the library call is a
+     pointer to the memory location.  Because the first argument is passed in
+     register D, we have to identify this, so that the first function
+     parameter is not passed in D either.  */
+  if (fntype == 0)
+    {
+      const char *name;
+      size_t len;
+
+      if (libname == 0 || GET_CODE (libname) != SYMBOL_REF)
+       return;
+
+      /* If the library ends in 'di' or in 'df', we assume it's
+         returning some DImode or some DFmode which are 64-bit wide.  */
+      name = XSTR (libname, 0);
+      len = strlen (name);
+      if (len > 3
+         && ((name[len - 2] == 'd'
+              && (name[len - 1] == 'f' || name[len - 1] == 'i'))
+             || (name[len - 3] == 'd'
+                 && (name[len - 2] == 'i' || name[len - 2] == 'f'))))
+       {
+         /* We are in.  Mark the first parameter register as already used.  */
+         cum->words = 1;
+         cum->nregs = 1;
+       }
+      return;
+    }
+
+  ret_type = TREE_TYPE (fntype);
+
+  if (ret_type && aggregate_value_p (ret_type))
+    {
+      cum->words = 1;
+      cum->nregs = 1;
+    }
+}
+
+/* Update the data in CUM to advance over an argument
+   of mode MODE and data type TYPE.
+   (TYPE is null for libcalls where that information may not be available.)  */
+
+void
+m68hc11_function_arg_advance (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named ATTRIBUTE_UNUSED;
+{
+  if (mode != BLKmode)
+    {
+      if (cum->words == 0 && GET_MODE_SIZE (mode) == 4)
+       {
+         cum->nregs = 2;
+         cum->words = GET_MODE_SIZE (mode);
+       }
+      else
+       {
+         cum->words += GET_MODE_SIZE (mode);
+         if (cum->words <= HARD_REG_SIZE)
+           cum->nregs = 1;
+       }
+    }
+  else
+    {
+      cum->words += int_size_in_bytes (type);
+    }
+  return;
+}
+
+/* Define where to put the arguments to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).  */
+
+struct rtx_def *
+m68hc11_function_arg (cum, mode, type, named)
+     const CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type ATTRIBUTE_UNUSED;
+     int named ATTRIBUTE_UNUSED;
+{
+  if (cum->words != 0)
+    {
+      return NULL_RTX;
+    }
+
+  if (mode != BLKmode)
+    {
+      if (GET_MODE_SIZE (mode) == 2 * HARD_REG_SIZE)
+       return gen_rtx (REG, mode, HARD_X_REGNUM);
+
+      if (GET_MODE_SIZE (mode) > HARD_REG_SIZE)
+       {
+         return NULL_RTX;
+       }
+      return gen_rtx (REG, mode, HARD_D_REGNUM);
+    }
+  return NULL_RTX;
+}
+
+#if GCC_VERSION > 2095
+
+/* The "standard" implementation of va_start: just assign `nextarg' to
+   the variable.  */
+void
+m68hc11_expand_builtin_va_start (stdarg_p, valist, nextarg)
+     int stdarg_p ATTRIBUTE_UNUSED;
+     tree valist;
+     rtx nextarg;
+{
+  tree t;
+
+  /* SCz: the default implementation in builtins.c adjust the
+     nextarg using UNITS_PER_WORD.  This works only with -mshort
+     and fails when integers are 32-bit.  Here is the correct way.  */
+  if (!stdarg_p)
+    nextarg = plus_constant (nextarg, -INT_TYPE_SIZE / 8);
+
+  t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
+            make_tree (ptr_type_node, nextarg));
+  TREE_SIDE_EFFECTS (t) = 1;
+
+  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+}
+
+rtx
+m68hc11_va_arg (valist, type)
+     tree valist;
+     tree type;
+{
+  tree addr_tree, t;
+  HOST_WIDE_INT align;
+  HOST_WIDE_INT rounded_size;
+  rtx addr;
+  int pad_direction;
+
+  /* Compute the rounded size of the type.  */
+  align = PARM_BOUNDARY / BITS_PER_UNIT;
+  rounded_size = (((int_size_in_bytes (type) + align - 1) / align) * align);
+
+  /* Get AP.  */
+  addr_tree = valist;
+  pad_direction = m68hc11_function_arg_padding (TYPE_MODE (type), type);
+
+  if (pad_direction == downward)
+    {
+      /* Small args are padded downward.  */
+
+      HOST_WIDE_INT adj;
+      adj = TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_UNIT;
+      if (rounded_size > align)
+       adj = rounded_size;
+
+      addr_tree = build (PLUS_EXPR, TREE_TYPE (addr_tree), addr_tree,
+                        build_int_2 (rounded_size - adj, 0));
+    }
+
+  addr = expand_expr (addr_tree, NULL_RTX, Pmode, EXPAND_NORMAL);
+  addr = copy_to_reg (addr);
+
+  /* Compute new value for AP.  */
+  t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
+            build (PLUS_EXPR, TREE_TYPE (valist), valist,
+                   build_int_2 (rounded_size, 0)));
+  TREE_SIDE_EFFECTS (t) = 1;
+  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+  return addr;
+}
+#endif
+
+/* If defined, a C expression which determines whether, and in which direction,
+   to pad out an argument with extra space.  The value should be of type
+   `enum direction': either `upward' to pad above the argument,
+   `downward' to pad below, or `none' to inhibit padding.
+
+   Structures are stored left shifted in their argument slot.  */
+int
+m68hc11_function_arg_padding (mode, type)
+     enum machine_mode mode;
+     tree type;
+{
+  if (type != 0 && AGGREGATE_TYPE_P (type))
+    return upward;
+
+  /* This is the default definition.  */
+  return (!BYTES_BIG_ENDIAN
+         ? upward
+         : ((mode == BLKmode
+             ? (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+                && int_size_in_bytes (type) <
+                (PARM_BOUNDARY / BITS_PER_UNIT)) : GET_MODE_BITSIZE (mode) <
+             PARM_BOUNDARY) ? downward : upward));
+}
+\f
+
+/* Function prologue and epilogue.  */
+
+/* Emit a move after the reload pass has completed.  This is used to
+   emit the prologue and epilogue.  */
+static void
+emit_move_after_reload (to, from, scratch)
+     rtx to, from, scratch;
+{
+  rtx insn;
+
+  if (TARGET_M6812 || H_REG_P (to) || H_REG_P (from))
+    {
+      insn = emit_move_insn (to, from);
+    }
+  else
+    {
+      emit_move_insn (scratch, from);
+      insn = emit_move_insn (to, scratch);
+    }
+
+  /* Put a REG_INC note to tell the flow analysis that the instruction
+     is necessary.  */
+  if (IS_STACK_PUSH (to))
+    {
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
+                                           XEXP (XEXP (to, 0), 0),
+                                           REG_NOTES (insn));
+    }
+  else if (IS_STACK_POP (from))
+    {
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC,
+                                           XEXP (XEXP (from, 0), 0),
+                                           REG_NOTES (insn));
+    }
+}
+
+int
+m68hc11_total_frame_size ()
+{
+  int size;
+  int regno;
+
+  size = get_frame_size ();
+  if (current_function_interrupt)
+    {
+      size += 3 * HARD_REG_SIZE;
+    }
+  if (frame_pointer_needed)
+    size += HARD_REG_SIZE;
+
+  for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
+    if (regs_ever_live[regno] && !call_used_regs[regno])
+      size += HARD_REG_SIZE;
+
+  return size;
+}
+
+void
+m68hc11_function_epilogue (out, size)
+     FILE *out ATTRIBUTE_UNUSED;
+     int size ATTRIBUTE_UNUSED;
+{
+  /* We catch the function epilogue generation to have a chance
+     to clear the z_replacement_completed flag.  */
+  z_replacement_completed = 0;
+}
+
+void
+expand_prologue ()
+{
+  tree func_attr;
+  int size;
+  int regno;
+  rtx scratch;
+
+  if (reload_completed != 1)
+    abort ();
+
+  size = get_frame_size ();
+
+  create_regs_rtx ();
+
+  /* Generate specific prologue for interrupt handlers.  */
+  func_attr = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
+  current_function_interrupt = lookup_attribute ("interrupt",
+                                                func_attr) != NULL_TREE;
+  current_function_trap = lookup_attribute ("trap", func_attr) != NULL_TREE;
+
+  /* Get the scratch register to build the frame and push registers.
+     If the first argument is a 32-bit quantity, the D+X registers
+     are used.  Use Y to compute the frame.  Otherwise, X is cheaper.
+     For 68HC12, this scratch register is not used.  */
+  if (current_function_args_info.nregs == 2)
+    scratch = iy_reg;
+  else
+    scratch = ix_reg;
+
+  /* For an interrupt handler, we must preserve _.tmp, _.z and _.xy.
+     Other soft registers in page0 need not to be saved because they
+     will be restored by C functions.  For a trap handler, we don't
+     need to preserve these registers because this is a synchronous call.  */
+  if (current_function_interrupt)
+    {
+      emit_move_after_reload (stack_push_word, m68hc11_soft_tmp_reg, scratch);
+      emit_move_after_reload (stack_push_word,
+                             gen_rtx (REG, HImode, SOFT_Z_REGNUM), scratch);
+      emit_move_after_reload (stack_push_word,
+                             gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM),
+                             scratch);
+    }
+
+  /* Save current stack frame.  */
+  if (frame_pointer_needed)
+    emit_move_after_reload (stack_push_word, hard_frame_pointer_rtx, scratch);
+
+  /* Allocate local variables.  */
+  if (TARGET_M6812 && size >= 2)
+    {
+      emit_insn (gen_addhi3 (stack_pointer_rtx,
+                            stack_pointer_rtx, GEN_INT (-size)));
+    }
+  else if (size > 8)
+    {
+      rtx insn;
+
+      insn = gen_rtx_PARALLEL
+       (VOIDmode,
+        gen_rtvec (2,
+                   gen_rtx_SET (VOIDmode,
+                                stack_pointer_rtx,
+                                gen_rtx_PLUS (HImode,
+                                              stack_pointer_rtx,
+                                              GEN_INT (-size))),
+                   gen_rtx_CLOBBER (VOIDmode, scratch)));
+      emit_insn (insn);
+    }
+  else
+    {
+      int i;
+
+      /* Allocate by pushing scratch values.  */
+      for (i = 2; i <= size; i += 2)
+       emit_move_after_reload (stack_push_word, ix_reg, 0);
+
+      if (size & 1)
+       emit_insn (gen_addhi3 (stack_pointer_rtx,
+                              stack_pointer_rtx, GEN_INT (-1)));
+    }
+
+  /* Create the frame pointer.  */
+  if (frame_pointer_needed)
+    emit_move_after_reload (hard_frame_pointer_rtx,
+                           stack_pointer_rtx, scratch);
+
+  /* Push any 2 byte pseudo hard registers that we need to save.  */
+  for (regno = SOFT_REG_FIRST; regno <= SOFT_REG_LAST; regno++)
+    {
+      if (regs_ever_live[regno] && !call_used_regs[regno])
+       {
+         emit_move_after_reload (stack_push_word,
+                                 gen_rtx (REG, HImode, regno), scratch);
+       }
+    }
+}
+
+void
+expand_epilogue ()
+{
+  int size;
+  register int regno;
+  int return_size;
+  rtx scratch;
+
+  if (reload_completed != 1)
+    abort ();
+
+  size = get_frame_size ();
+
+  /* If we are returning a value in two registers, we have to preserve the
+     X register and use the Y register to restore the stack and the saved
+     registers.  Otherwise, use X because it's faster (and smaller).  */
+  if (current_function_return_rtx == 0)
+    return_size = 0;
+  else if (GET_CODE (current_function_return_rtx) == MEM)
+    return_size = HARD_REG_SIZE;
+  else
+    return_size = GET_MODE_SIZE (GET_MODE (current_function_return_rtx));
+
+  if (return_size > HARD_REG_SIZE)
+    scratch = iy_reg;
+  else
+    scratch = ix_reg;
+
+  /* Pop any 2 byte pseudo hard registers that we saved.  */
+  for (regno = SOFT_REG_LAST; regno >= SOFT_REG_FIRST; regno--)
+    {
+      if (regs_ever_live[regno] && !call_used_regs[regno])
+       {
+         emit_move_after_reload (gen_rtx (REG, HImode, regno),
+                                 stack_pop_word, scratch);
+       }
+    }
+
+  /* de-allocate auto variables */
+  if (TARGET_M6812 && size >= 2)
+    {
+      emit_insn (gen_addhi3 (stack_pointer_rtx,
+                            stack_pointer_rtx, GEN_INT (size)));
+    }
+  else if (size > 8)
+    {
+      rtx insn;
+
+      insn = gen_rtx_PARALLEL
+       (VOIDmode,
+        gen_rtvec (2,
+                   gen_rtx_SET (VOIDmode,
+                                stack_pointer_rtx,
+                                gen_rtx_PLUS (HImode,
+                                              stack_pointer_rtx,
+                                              GEN_INT (size))),
+                   gen_rtx_CLOBBER (VOIDmode, scratch)));
+      emit_insn (insn);
+    }
+  else
+    {
+      int i;
+
+      for (i = 2; i <= size; i += 2)
+       emit_move_after_reload (scratch, stack_pop_word, scratch);
+      if (size & 1)
+       emit_insn (gen_addhi3 (stack_pointer_rtx,
+                              stack_pointer_rtx, GEN_INT (1)));
+    }
+
+  /* Restore previous frame pointer.  */
+  if (frame_pointer_needed)
+    emit_move_after_reload (hard_frame_pointer_rtx, stack_pop_word, scratch);
+
+  /* For an interrupt handler, restore ZTMP, ZREG and XYREG.  */
+  if (current_function_interrupt)
+    {
+      emit_move_after_reload (gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM),
+                             stack_pop_word, scratch);
+      emit_move_after_reload (gen_rtx (REG, HImode, SOFT_Z_REGNUM),
+                             stack_pop_word, scratch);
+      emit_move_after_reload (m68hc11_soft_tmp_reg, stack_pop_word, scratch);
+    }
+
+  /* If the trap handler returns some value, copy the value
+     in D, X onto the stack so that the rti will pop the return value
+     correctly.  */
+  else if (current_function_trap && return_size != 0)
+    {
+      rtx addr_reg = stack_pointer_rtx;
+
+      if (!TARGET_M6812)
+       {
+         emit_move_after_reload (scratch, stack_pointer_rtx, 0);
+         addr_reg = scratch;
+       }
+      emit_move_after_reload (gen_rtx (MEM, HImode,
+                                      gen_rtx (PLUS, HImode, addr_reg,
+                                               GEN_INT (1))), d_reg, 0);
+      if (return_size > HARD_REG_SIZE)
+       emit_move_after_reload (gen_rtx (MEM, HImode,
+                                        gen_rtx (PLUS, HImode, addr_reg,
+                                                 GEN_INT (3))), ix_reg, 0);
+    }
+
+  emit_jump_insn (gen_return ());
+}
+\f
+
+/* Low and High part extraction for 68HC11.  These routines are
+   similar to gen_lowpart and gen_highpart but they have been
+   fixed to work for constants and 68HC11 specific registers.  */
+
+rtx
+m68hc11_gen_lowpart (mode, x)
+     enum machine_mode mode;
+     rtx x;
+{
+  /* We assume that the low part of an auto-inc mode is the same with
+     the mode changed and that the caller split the larger mode in the
+     correct order.  */
+  if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
+    {
+      return gen_rtx (MEM, mode, XEXP (x, 0));
+    }
+
+  /* Note that a CONST_DOUBLE rtx could represent either an integer or a
+     floating-point constant.  A CONST_DOUBLE is used whenever the
+     constant requires more than one word in order to be adequately
+     represented.  */
+  if (GET_CODE (x) == CONST_DOUBLE)
+    {
+      long l[2];
+
+      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+       {
+         REAL_VALUE_TYPE r;
+
+         if (GET_MODE (x) == SFmode)
+           {
+             REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+             REAL_VALUE_TO_TARGET_SINGLE (r, l[0]);
+           }
+         else
+           {
+             rtx first, second;
+
+             split_double (x, &first, &second);
+             return second;
+           }
+         if (mode == SImode)
+           return gen_rtx (CONST_INT, VOIDmode, l[0]);
+
+         return gen_rtx (CONST_INT, VOIDmode, l[0] & 0x0ffff);
+       }
+      else
+       {
+         l[0] = CONST_DOUBLE_LOW (x);
+       }
+      if (mode == SImode)
+       return gen_rtx (CONST_INT, VOIDmode, l[0]);
+      else if (mode == HImode && GET_MODE (x) == SFmode)
+       return gen_rtx (CONST_INT, VOIDmode, l[0] & 0x0FFFF);
+      else
+       abort ();
+    }
+
+  if (mode == QImode && D_REG_P (x))
+    return gen_rtx (REG, mode, HARD_B_REGNUM);
+
+  /* gen_lowpart crashes when it is called with a SUBREG.  */
+  if (GET_CODE (x) == SUBREG && SUBREG_WORD (x) != 0)
+    {
+      if (mode == SImode)
+       return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_WORD (x) + 2);
+      else if (mode == HImode)
+       return gen_rtx_SUBREG (mode, SUBREG_REG (x), SUBREG_WORD (x) + 1);
+      else
+       abort ();
+    }
+  x = gen_lowpart (mode, x);
+
+  /* Return a different rtx to avoid to share it in several insns
+     (when used by a split pattern).  Sharing addresses within
+     a MEM breaks the Z register replacement (and reloading).  */
+  if (GET_CODE (x) == MEM)
+    x = copy_rtx (x);
+  return x;
+}
+
+rtx
+m68hc11_gen_highpart (mode, x)
+     enum machine_mode mode;
+     rtx x;
+{
+  /* We assume that the high part of an auto-inc mode is the same with
+     the mode changed and that the caller split the larger mode in the
+     correct order.  */
+  if (GET_CODE (x) == MEM && m68hc11_auto_inc_p (XEXP (x, 0)))
+    {
+      return gen_rtx (MEM, mode, XEXP (x, 0));
+    }
+
+  /* Note that a CONST_DOUBLE rtx could represent either an integer or a
+     floating-point constant.  A CONST_DOUBLE is used whenever the
+     constant requires more than one word in order to be adequately
+     represented.  */
+  if (GET_CODE (x) == CONST_DOUBLE)
+    {
+      long l[2];
+
+      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+       {
+         REAL_VALUE_TYPE r;
+
+         if (GET_MODE (x) == SFmode)
+           {
+             REAL_VALUE_FROM_CONST_DOUBLE (r, x);
+             REAL_VALUE_TO_TARGET_SINGLE (r, l[1]);
+           }
+         else
+           {
+             rtx first, second;
+
+             split_double (x, &first, &second);
+             return first;
+           }
+         if (mode == SImode)
+           return gen_rtx (CONST_INT, VOIDmode, l[1]);
+
+         return gen_rtx (CONST_INT, VOIDmode, (l[1] >> 16) & 0x0ffff);
+       }
+      else
+       {
+         l[1] = CONST_DOUBLE_HIGH (x);
+       }
+
+      if (mode == SImode)
+       return gen_rtx (CONST_INT, VOIDmode, l[1]);
+      else if (mode == HImode && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+       return gen_rtx (CONST_INT, VOIDmode, (l[0] >> 16) & 0x0FFFF);
+      else
+       abort ();
+    }
+  if (GET_CODE (x) == CONST_INT)
+    {
+      HOST_WIDE_INT val = INTVAL (x);
+
+      if (mode == QImode)
+       {
+         return gen_rtx (CONST_INT, VOIDmode, val >> 8);
+       }
+      else if (mode == HImode)
+       {
+         return gen_rtx (CONST_INT, VOIDmode, val >> 16);
+       }
+    }
+  if (mode == QImode && D_REG_P (x))
+    return gen_rtx (REG, mode, HARD_A_REGNUM);
+
+  /* There is no way in GCC to represent the upper part of a word register.
+     To obtain the 8-bit upper part of a soft register, we change the
+     reg into a mem rtx.  This is possible because they are physically
+     located in memory.  There is no offset because we are big-endian.  */
+  if (mode == QImode && S_REG_P (x))
+    {
+      int pos;
+
+      /* For 68HC12, avoid the '*' for direct addressing mode.  */
+      pos = TARGET_M6812 ? 1 : 0;
+      return gen_rtx (MEM, QImode,
+                     gen_rtx (SYMBOL_REF, Pmode,
+                              &reg_names[REGNO (x)][pos]));
+    }
+
+  /* gen_highpart crashes when it is called with a SUBREG.  */
+  if (GET_CODE (x) == SUBREG && SUBREG_WORD (x) != 0)
+    {
+      return gen_rtx (SUBREG, mode, XEXP (x, 0), XEXP (x, 1));
+    }
+  x = gen_highpart (mode, x);
+
+  /* Return a different rtx to avoid to share it in several insns
+     (when used by a split pattern).  Sharing addresses within
+     a MEM breaks the Z register replacement (and reloading).  */
+  if (GET_CODE (x) == MEM)
+    x = copy_rtx (x);
+  return x;
+}
+\f
+
+/* Obscure register manipulation.  */
+
+/* Finds backward in the instructions to see if register 'reg' is
+   dead.  This is used when generating code to see if we can use 'reg'
+   as a scratch register.  This allows us to choose a better generation
+   of code when we know that some register dies or can be clobbered.  */
+
+int
+dead_register_here (x, reg)
+     rtx x;
+     rtx reg;
+{
+  rtx x_reg;
+  rtx p;
+
+  if (D_REG_P (reg))
+    x_reg = gen_rtx (REG, SImode, HARD_X_REGNUM);
+  else
+    x_reg = 0;
+
+  for (p = PREV_INSN (x); p && GET_CODE (p) != CODE_LABEL; p = PREV_INSN (p))
+    if (GET_RTX_CLASS (GET_CODE (p)) == 'i')
+      {
+       rtx body;
+
+       body = PATTERN (p);
+
+       if (GET_CODE (body) == CALL_INSN)
+         break;
+       if (GET_CODE (body) == JUMP_INSN)
+         break;
+
+       if (GET_CODE (body) == SET)
+         {
+           rtx dst = XEXP (body, 0);
+
+           if (GET_CODE (dst) == REG && REGNO (dst) == REGNO (reg))
+             break;
+           if (x_reg && rtx_equal_p (dst, x_reg))
+             break;
+
+           if (find_regno_note (p, REG_DEAD, REGNO (reg)))
+             return 1;
+         }
+       else if (reg_mentioned_p (reg, p)
+                || (x_reg && reg_mentioned_p (x_reg, p)))
+         break;
+      }
+
+  /* Scan forward to see if the register is set in some insns and never
+     used since then. */
+  for (p = x /*NEXT_INSN (x) */ ; p; p = NEXT_INSN (p))
+    {
+      rtx body;
+
+      if (GET_CODE (p) == CODE_LABEL
+         || GET_CODE (p) == JUMP_INSN
+         || GET_CODE (p) == CALL_INSN || GET_CODE (p) == BARRIER)
+       break;
+
+      if (GET_CODE (p) != INSN)
+       continue;
+
+      body = PATTERN (p);
+      if (GET_CODE (body) == SET)
+       {
+         rtx src = XEXP (body, 1);
+         rtx dst = XEXP (body, 0);
+
+         if (GET_CODE (dst) == REG
+             && REGNO (dst) == REGNO (reg) && !reg_mentioned_p (reg, src))
+           return 1;
+       }
+
+      /* Register is used (may be in source or in dest). */
+      if (reg_mentioned_p (reg, p)
+         || (x_reg != 0 && GET_MODE (p) == SImode
+             && reg_mentioned_p (x_reg, p)))
+       break;
+    }
+  return p == 0 ? 1 : 0;
+}
+\f
+
+/* Code generation operations called from machine description file.  */
+
+/* Print the name of register 'regno' in the assembly file.  */
+static void
+asm_print_register (file, regno)
+     FILE *file;
+     int regno;
+{
+  const char *name = reg_names[regno];
+
+  if (TARGET_M6812 && name[0] == '*')
+    name++;
+
+  asm_fprintf (file, "%s", name);
+}
+
+/* A C compound statement to output to stdio stream STREAM the
+   assembler syntax for an instruction operand X.  X is an RTL
+   expression.
+
+   CODE is a value that can be used to specify one of several ways
+   of printing the operand.  It is used when identical operands
+   must be printed differently depending on the context.  CODE
+   comes from the `%' specification that was used to request
+   printing of the operand.  If the specification was just `%DIGIT'
+   then CODE is 0; if the specification was `%LTR DIGIT' then CODE
+   is the ASCII code for LTR.
+
+   If X is a register, this macro should print the register's name.
+   The names can be found in an array `reg_names' whose type is
+   `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
+
+   When the machine description has a specification `%PUNCT' (a `%'
+   followed by a punctuation character), this macro is called with
+   a null pointer for X and the punctuation character for CODE.
+
+   The M68HC11 specific codes are:
+
+   'b' for the low part of the operand.
+   'h' for the high part of the operand
+       The 'b' or 'h' modifiers have no effect if the operand has
+       the QImode and is not a S_REG_P (soft register).  If the
+       operand is a hard register, these two modifiers have no effect.
+   't' generate the temporary scratch register.  The operand is
+       ignored.
+   'T' generate the low-part temporary scratch register.  The operand is
+       ignored.   */
+
+void
+print_operand (file, op, letter)
+     FILE *file;
+     rtx op;
+     int letter;
+{
+  if (letter == 't')
+    {
+      asm_print_register (file, SOFT_TMP_REGNUM);
+      return;
+    }
+  else if (letter == 'T')
+    {
+      asm_print_register (file, SOFT_TMP_REGNUM);
+      asm_fprintf (file, "+1");
+      return;
+    }
+  else if (letter == '#')
+    {
+      asm_fprintf (file, "%0I");
+    }
+
+  if (GET_CODE (op) == REG)
+    {
+      if (letter == 'b' && S_REG_P (op))
+       {
+         asm_print_register (file, REGNO (op));
+         asm_fprintf (file, "+1");
+       }
+      else
+       {
+         asm_print_register (file, REGNO (op));
+       }
+      return;
+    }
+
+  if (GET_CODE (op) == SYMBOL_REF && (letter == 'b' || letter == 'h'))
+    {
+      if (letter == 'b')
+       asm_fprintf (file, "%0I%%lo(");
+      else
+       asm_fprintf (file, "%0I%%hi(");
+
+      output_addr_const (file, op);
+      asm_fprintf (file, ")");
+      return;
+    }
+
+  /* Get the low or high part of the operand when 'b' or 'h' modifiers
+     are specified.  If we already have a QImode, there is nothing to do.  */
+  if (GET_MODE (op) == HImode || GET_MODE (op) == VOIDmode)
+    {
+      if (letter == 'b')
+       {
+         op = m68hc11_gen_lowpart (QImode, op);
+       }
+      else if (letter == 'h')
+       {
+         op = m68hc11_gen_highpart (QImode, op);
+       }
+    }
+
+  if (GET_CODE (op) == MEM)
+    {
+      rtx base = XEXP (op, 0);
+      switch (GET_CODE (base))
+       {
+       case PRE_DEC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (op)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+           }
+         else
+           abort ();
+         break;
+
+       case POST_DEC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+             asm_fprintf (file, "-");
+           }
+         else
+           abort ();
+         break;
+
+       case POST_INC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (op)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+             asm_fprintf (file, "+");
+           }
+         else
+           abort ();
+         break;
+
+       case PRE_INC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (op)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+           }
+         else
+           abort ();
+         break;
+
+       default:
+         output_address (base);
+         break;
+       }
+    }
+  else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == SFmode)
+    {
+      REAL_VALUE_TYPE r;
+      REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+      ASM_OUTPUT_FLOAT_OPERAND (letter, file, r);
+    }
+  else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == XFmode)
+    {
+      REAL_VALUE_TYPE r;
+      REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+      ASM_OUTPUT_LONG_DOUBLE_OPERAND (file, r);
+    }
+  else if (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == DFmode)
+    {
+      REAL_VALUE_TYPE r;
+      REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+      ASM_OUTPUT_DOUBLE_OPERAND (file, r);
+    }
+  else
+    {
+      if (letter != 'i')
+       asm_fprintf (file, "%0I");
+      output_addr_const (file, op);
+    }
+}
+
+/* Returns true if the operand 'op' must be printed with parenthesis
+   arround it.  This must be done only if there is a symbol whose name
+   is a processor register.  */
+static int
+must_parenthesize (op)
+     rtx op;
+{
+  const char *name;
+
+  switch (GET_CODE (op))
+    {
+    case SYMBOL_REF:
+      name = XSTR (op, 0);
+      /* Avoid a conflict between symbol name and a possible
+         register.  */
+      return (strcasecmp (name, "a") == 0
+             || strcasecmp (name, "b") == 0
+             || strcasecmp (name, "d") == 0
+             || strcasecmp (name, "x") == 0
+             || strcasecmp (name, "y") == 0
+             || strcasecmp (name, "pc") == 0
+             || strcasecmp (name, "sp") == 0
+             || strcasecmp (name, "ccr") == 0) ? 1 : 0;
+
+    case PLUS:
+    case MINUS:
+      return must_parenthesize (XEXP (op, 0))
+       || must_parenthesize (XEXP (op, 1));
+
+    case MEM:
+    case CONST:
+    case ZERO_EXTEND:
+    case SIGN_EXTEND:
+      return must_parenthesize (XEXP (op, 0));
+
+    case CONST_DOUBLE:
+    case CONST_INT:
+    case LABEL_REF:
+    case CODE_LABEL:
+    default:
+      return 0;
+    }
+}
+
+/* A C compound statement to output to stdio stream STREAM the
+   assembler syntax for an instruction operand that is a memory
+   reference whose address is ADDR.  ADDR is an RTL expression.  */
+
+void
+print_operand_address (file, addr)
+     FILE *file;
+     rtx addr;
+{
+  rtx base;
+  rtx offset;
+  int need_parenthesis = 0;
+
+  switch (GET_CODE (addr))
+    {
+    case REG:
+      if (!REG_P (addr) || !REG_OK_FOR_BASE_STRICT_P (addr))
+       abort ();
+
+      asm_fprintf (file, "0,");
+      asm_print_register (file, REGNO (addr));
+      break;
+
+    case MEM:
+      base = XEXP (addr, 0);
+      switch (GET_CODE (base))
+       {
+       case PRE_DEC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,-", GET_MODE_SIZE (GET_MODE (addr)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+           }
+         else
+           abort ();
+         break;
+
+       case POST_DEC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+             asm_fprintf (file, "-");
+           }
+         else
+           abort ();
+         break;
+
+       case POST_INC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,", GET_MODE_SIZE (GET_MODE (addr)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+             asm_fprintf (file, "+");
+           }
+         else
+           abort ();
+         break;
+
+       case PRE_INC:
+         if (TARGET_M6812)
+           {
+             asm_fprintf (file, "%u,+", GET_MODE_SIZE (GET_MODE (addr)));
+             asm_print_register (file, REGNO (XEXP (base, 0)));
+           }
+         else
+           abort ();
+         break;
+
+       default:
+         need_parenthesis = must_parenthesize (base);
+         if (need_parenthesis)
+           asm_fprintf (file, "(");
+
+         output_addr_const (file, base);
+         if (need_parenthesis)
+           asm_fprintf (file, ")");
+         break;
+       }
+      break;
+
+    case PLUS:
+      base = XEXP (addr, 0);
+      offset = XEXP (addr, 1);
+      if (!G_REG_P (base) && G_REG_P (offset))
+       {
+         base = XEXP (addr, 1);
+         offset = XEXP (addr, 0);
+       }
+      if ((CONSTANT_ADDRESS_P (base)) && (CONSTANT_ADDRESS_P (offset)))
+       {
+         need_parenthesis = must_parenthesize (addr);
+
+         if (need_parenthesis)
+           asm_fprintf (file, "(");
+
+         output_addr_const (file, base);
+         asm_fprintf (file, "+");
+         output_addr_const (file, offset);
+         if (need_parenthesis)
+           asm_fprintf (file, ")");
+       }
+      else if (REG_P (base) && REG_OK_FOR_BASE_STRICT_P (base))
+       {
+         if (REG_P (offset))
+           {
+             if (TARGET_M6812)
+               {
+                 asm_print_register (file, REGNO (offset));
+                 asm_fprintf (file, ",");
+                 asm_print_register (file, REGNO (base));
+               }
+             else
+               abort ();
+           }
+         else
+           {
+             output_addr_const (file, offset);
+             asm_fprintf (file, ",");
+             asm_print_register (file, REGNO (base));
+           }
+       }
+      else
+       {
+         abort ();
+       }
+      break;
+
+    default:
+      if (GET_CODE (addr) == CONST_INT
+         && INTVAL (addr) < 0x8000 && INTVAL (addr) >= -0x8000)
+       {
+         asm_fprintf (file, "%d", INTVAL (addr));
+       }
+      else
+       {
+         need_parenthesis = must_parenthesize (addr);
+         if (need_parenthesis)
+           asm_fprintf (file, "(");
+
+         output_addr_const (file, addr);
+         if (need_parenthesis)
+           asm_fprintf (file, ")");
+       }
+      break;
+    }
+}
+\f
+
+/* Splitting of some instructions.  */
+
+static rtx
+m68hc11_expand_compare (code, op0, op1)
+     enum rtx_code code;
+     rtx op0, op1;
+{
+  rtx ret = 0;
+
+  if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_FLOAT)
+    abort ();
+  else
+    {
+      emit_insn (gen_rtx_SET (VOIDmode, cc0_rtx,
+                             gen_rtx_COMPARE (VOIDmode, op0, op1)));
+      ret = gen_rtx (code, VOIDmode, cc0_rtx, const0_rtx);
+    }
+
+  return ret;
+}
+
+rtx
+m68hc11_expand_compare_and_branch (code, op0, op1, label)
+     enum rtx_code code;
+     rtx op0, op1, label;
+{
+  rtx tmp;
+
+  switch (GET_MODE (op0))
+    {
+    case QImode:
+    case HImode:
+      tmp = m68hc11_expand_compare (code, op0, op1);
+      tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+                                 gen_rtx_LABEL_REF (VOIDmode, label),
+                                 pc_rtx);
+      emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, tmp));
+      return 0;
+#if 0
+
+      /* SCz: from i386.c  */
+    case SFmode:
+    case DFmode:
+      /* Don't expand the comparison early, so that we get better code
+         when jump or whoever decides to reverse the comparison.  */
+      {
+       rtvec vec;
+       int use_fcomi;
+
+       code = m68hc11_prepare_fp_compare_args (code, &m68hc11_compare_op0,
+                                               &m68hc11_compare_op1);
+
+       tmp = gen_rtx_fmt_ee (code, m68hc11_fp_compare_mode (code),
+                             m68hc11_compare_op0, m68hc11_compare_op1);
+       tmp = gen_rtx_IF_THEN_ELSE (VOIDmode, tmp,
+                                   gen_rtx_LABEL_REF (VOIDmode, label),
+                                   pc_rtx);
+       tmp = gen_rtx_SET (VOIDmode, pc_rtx, tmp);
+
+       use_fcomi = ix86_use_fcomi_compare (code);
+       vec = rtvec_alloc (3 + !use_fcomi);
+       RTVEC_ELT (vec, 0) = tmp;
+       RTVEC_ELT (vec, 1)
+         = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 18));
+       RTVEC_ELT (vec, 2)
+         = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCFPmode, 17));
+       if (!use_fcomi)
+         RTVEC_ELT (vec, 3)
+           = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (HImode));
+
+       emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, vec));
+       return;
+      }
+#endif
+
+    case SImode:
+      /* Expand SImode branch into multiple compare+branch.  */
+      {
+       rtx lo[2], hi[2], label2;
+       enum rtx_code code1, code2, code3;
+
+       if (CONSTANT_P (op0) && !CONSTANT_P (op1))
+         {
+           tmp = op0;
+           op0 = op1;
+           op1 = tmp;
+           code = swap_condition (code);
+         }
+       lo[0] = m68hc11_gen_lowpart (HImode, op0);
+       lo[1] = m68hc11_gen_lowpart (HImode, op1);
+       hi[0] = m68hc11_gen_highpart (HImode, op0);
+       hi[1] = m68hc11_gen_highpart (HImode, op1);
+
+       /* Otherwise, if we are doing less-than, op1 is a constant and the
+          low word is zero, then we can just examine the high word.  */
+
+       if (GET_CODE (hi[1]) == CONST_INT && lo[1] == const0_rtx
+           && (code == LT || code == LTU))
+         {
+           return m68hc11_expand_compare_and_branch (code, hi[0], hi[1],
+                                                     label);
+         }
+
+       /* Otherwise, we need two or three jumps.  */
+
+       label2 = gen_label_rtx ();
+
+       code1 = code;
+       code2 = swap_condition (code);
+       code3 = unsigned_condition (code);
+
+       switch (code)
+         {
+         case LT:
+         case GT:
+         case LTU:
+         case GTU:
+           break;
+
+         case LE:
+           code1 = LT;
+           code2 = GT;
+           break;
+         case GE:
+           code1 = GT;
+           code2 = LT;
+           break;
+         case LEU:
+           code1 = LTU;
+           code2 = GTU;
+           break;
+         case GEU:
+           code1 = GTU;
+           code2 = LTU;
+           break;
+
+         case EQ:
+           code1 = NIL;
+           code2 = NE;
+           break;
+         case NE:
+           code2 = NIL;
+           break;
+
+         default:
+           abort ();
+         }
+
+       /*
+        * a < b =>
+        *    if (hi(a) < hi(b)) goto true;
+        *    if (hi(a) > hi(b)) goto false;
+        *    if (lo(a) < lo(b)) goto true;
+        *  false:
+        */
+       if (code1 != NIL)
+         m68hc11_expand_compare_and_branch (code1, hi[0], hi[1], label);
+       if (code2 != NIL)
+         m68hc11_expand_compare_and_branch (code2, hi[0], hi[1], label2);
+
+       m68hc11_expand_compare_and_branch (code3, lo[0], lo[1], label);
+
+       if (code2 != NIL)
+         emit_label (label2);
+       return 0;
+      }
+
+    default:
+      abort ();
+    }
+  return 0;
+}
+
+
+/* Split a DI, SI or HI move into several smaller move operations.
+   The scratch register 'scratch' is used as a temporary to load
+   store intermediate values.  It must be a hard register.  */
+void
+m68hc11_split_move (to, from, scratch)
+     rtx to, from, scratch;
+{
+  rtx low_to, low_from;
+  rtx high_to, high_from;
+  enum machine_mode mode;
+
+  mode = GET_MODE (to);
+  if (GET_MODE_SIZE (mode) == 8)
+    mode = SImode;
+  else if (GET_MODE_SIZE (mode) == 4)
+    mode = HImode;
+  else
+    mode = QImode;
+
+  low_to = m68hc11_gen_lowpart (mode, to);
+  high_to = m68hc11_gen_highpart (mode, to);
+
+  low_from = m68hc11_gen_lowpart (mode, from);
+  if (mode == SImode && GET_CODE (from) == CONST_INT)
+    {
+      if (INTVAL (from) >= 0)
+       high_from = const0_rtx;
+      else
+       high_from = constm1_rtx;
+    }
+  else
+    high_from = m68hc11_gen_highpart (mode, from);
+
+  if (mode == SImode)
+    {
+      m68hc11_split_move (low_to, low_from, scratch);
+      m68hc11_split_move (high_to, high_from, scratch);
+    }
+  else if (H_REG_P (to) || H_REG_P (from)
+          || (TARGET_M6812
+              && (!m68hc11_register_indirect_p (from, GET_MODE (from))
+                  || m68hc11_small_indexed_indirect_p (from,
+                                                       GET_MODE (from)))
+              && (!m68hc11_register_indirect_p (to, GET_MODE (to))
+                  || m68hc11_small_indexed_indirect_p (to, GET_MODE (to)))))
+    {
+      emit_move_insn (low_to, low_from);
+      emit_move_insn (high_to, high_from);
+    }
+  else
+    {
+      rtx insn;
+
+      emit_move_insn (scratch, low_from);
+      insn = emit_move_insn (low_to, scratch);
+
+      emit_move_insn (scratch, high_from);
+      insn = emit_move_insn (high_to, scratch);
+    }
+}
+
+static rtx
+simplify_logical (mode, code, operand, result)
+     enum machine_mode mode;
+     int code;
+     rtx operand;
+     rtx *result;
+{
+  int val;
+  int mask;
+
+  *result = 0;
+  if (GET_CODE (operand) != CONST_INT)
+    return operand;
+
+  if (mode == HImode)
+    mask = 0x0ffff;
+  else
+    mask = 0x0ff;
+
+  val = INTVAL (operand);
+  switch (code)
+    {
+    case IOR:
+      if ((val & mask) == 0)
+       return 0;
+      if ((val & mask) == mask)
+       *result = constm1_rtx;
+      break;
+
+    case AND:
+      if ((val & mask) == 0)
+       *result = const0_rtx;
+      if ((val & mask) == mask)
+       return 0;
+      break;
+
+    case XOR:
+      if ((val & mask) == 0)
+       return 0;
+      break;
+    }
+  return operand;
+}
+
+static void
+m68hc11_emit_logical (mode, code, operands)
+     enum machine_mode mode;
+     int code;
+     rtx *operands;
+{
+  rtx result;
+  int need_copy;
+
+  need_copy = (rtx_equal_p (operands[0], operands[1])
+              || rtx_equal_p (operands[0], operands[2])) ? 0 : 1;
+
+  operands[1] = simplify_logical (mode, code, operands[1], &result);
+  operands[2] = simplify_logical (mode, code, operands[2], &result);
+
+  if (result && GET_CODE (result) == CONST_INT)
+    {
+      if (!H_REG_P (operands[0]) && operands[3]
+         && (INTVAL (result) != 0 || IS_STACK_PUSH (operands[0])))
+       {
+         emit_move_insn (operands[3], result);
+         emit_move_insn (operands[0], operands[3]);
+       }
+      else
+       {
+         emit_move_insn (operands[0], result);
+       }
+    }
+  else if (operands[1] != 0 && operands[2] != 0)
+    {
+      rtx insn;
+
+      if (!H_REG_P (operands[0]) && operands[3])
+       {
+         emit_move_insn (operands[3], operands[1]);
+         emit_insn (gen_rtx (SET, mode,
+                             operands[3],
+                             gen_rtx (code, mode,
+                                      operands[3], operands[2])));
+         insn = emit_move_insn (operands[0], operands[3]);
+       }
+      else
+       {
+         insn = emit_insn (gen_rtx (SET, mode,
+                                    operands[0],
+                                    gen_rtx (code, mode,
+                                             operands[0], operands[2])));
+       }
+    }
+
+  /* The logical operation is similar to a copy. */
+  else if (need_copy)
+    {
+      rtx src;
+
+      if (GET_CODE (operands[1]) == CONST_INT)
+       src = operands[2];
+      else
+       src = operands[1];
+
+      if (!H_REG_P (operands[0]) && !H_REG_P (src))
+       {
+         emit_move_insn (operands[3], src);
+         emit_move_insn (operands[0], operands[3]);
+       }
+      else
+       {
+         emit_move_insn (operands[0], src);
+       }
+    }
+}
+
+void
+m68hc11_split_logical (mode, code, operands)
+     enum machine_mode mode;
+     int code;
+     rtx *operands;
+{
+  rtx low[4];
+  rtx high[4];
+
+  low[0] = m68hc11_gen_lowpart (mode, operands[0]);
+  low[1] = m68hc11_gen_lowpart (mode, operands[1]);
+  low[2] = m68hc11_gen_lowpart (mode, operands[2]);
+
+  high[0] = m68hc11_gen_highpart (mode, operands[0]);
+
+  if (mode == SImode && GET_CODE (operands[1]) == CONST_INT)
+    {
+      if (INTVAL (operands[1]) >= 0)
+       high[1] = const0_rtx;
+      else
+       high[1] = constm1_rtx;
+    }
+  else
+    high[1] = m68hc11_gen_highpart (mode, operands[1]);
+
+  if (mode == SImode && GET_CODE (operands[2]) == CONST_INT)
+    {
+      if (INTVAL (operands[2]) >= 0)
+       high[2] = const0_rtx;
+      else
+       high[2] = constm1_rtx;
+    }
+  else
+    high[2] = m68hc11_gen_highpart (mode, operands[2]);
+
+  low[3] = operands[3];
+  high[3] = operands[3];
+  if (mode == SImode)
+    {
+      m68hc11_split_logical (HImode, code, low);
+      m68hc11_split_logical (HImode, code, high);
+      return;
+    }
+
+  m68hc11_emit_logical (mode, code, low);
+  m68hc11_emit_logical (mode, code, high);
+}
+\f
+
+/* Code generation.  */
+
+void
+m68hc11_output_swap (insn, operands)
+     rtx insn ATTRIBUTE_UNUSED;
+     rtx operands[];
+{
+  /* We have to be careful with the cc_status.  An address register swap
+     is generated for some comparison.  The comparison is made with D
+     but the branch really uses the address register.  See the split
+     pattern for compare.  The xgdx/xgdy preserve the flags but after
+     the exchange, the flags will reflect to the value of X and not D.
+     Tell this by setting the cc_status according to the cc_prev_status.  */
+  if (X_REG_P (operands[1]) || X_REG_P (operands[0]))
+    {
+      if (cc_prev_status.value1 != 0
+         && (D_REG_P (cc_prev_status.value1)
+             || X_REG_P (cc_prev_status.value1)))
+       {
+         cc_status = cc_prev_status;
+         if (D_REG_P (cc_status.value1))
+           cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
+                                       HARD_X_REGNUM);
+         else
+           cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
+                                       HARD_D_REGNUM);
+       }
+      else
+       CC_STATUS_INIT;
+
+      output_asm_insn ("xgdx", operands);
+    }
+  else
+    {
+      if (cc_prev_status.value1 != 0
+         && (D_REG_P (cc_prev_status.value1)
+             || Y_REG_P (cc_prev_status.value1)))
+       {
+         cc_status = cc_prev_status;
+         if (D_REG_P (cc_status.value1))
+           cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
+                                       HARD_Y_REGNUM);
+         else
+           cc_status.value1 = gen_rtx (REG, GET_MODE (cc_status.value1),
+                                       HARD_D_REGNUM);
+       }
+      else
+       CC_STATUS_INIT;
+
+      output_asm_insn ("xgdy", operands);
+    }
+}
+
+/* Returns 1 if the next insn after 'insn' is a test of the register 'reg'.
+   This is used to decide whether a move that set flags should be used
+   instead.  */
+int
+next_insn_test_reg (insn, reg)
+     rtx insn;
+     rtx reg;
+{
+  rtx body;
+
+  insn = next_nonnote_insn (insn);
+  if (GET_CODE (insn) != INSN)
+    return 0;
+
+  body = PATTERN (insn);
+  if (sets_cc0_p (body) != 1)
+    return 0;
+
+  if (rtx_equal_p (XEXP (body, 1), reg) == 0)
+    return 0;
+
+  return 1;
+}
+
+/* Generate the code to move a 16-bit operand into another one.  */
+
+void
+m68hc11_gen_movhi (insn, operands)
+     rtx insn;
+     rtx *operands;
+{
+  int reg;
+
+  /* Move a register or memory to the same location.
+     This is possible because such insn can appear
+     in a non-optimizing mode.  */
+  if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
+    {
+      cc_status = cc_prev_status;
+      return;
+    }
+
+  if (TARGET_M6812)
+    {
+      if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
+       {
+         switch (REGNO (operands[1]))
+           {
+           case HARD_X_REGNUM:
+           case HARD_Y_REGNUM:
+           case HARD_D_REGNUM:
+             output_asm_insn ("psh%1", operands);
+             break;
+           default:
+             abort ();
+           }
+         return;
+       }
+      if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
+       {
+         switch (REGNO (operands[0]))
+           {
+           case HARD_X_REGNUM:
+           case HARD_Y_REGNUM:
+           case HARD_D_REGNUM:
+             output_asm_insn ("pul%0", operands);
+             break;
+           default:
+             abort ();
+           }
+         return;
+       }
+      if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
+       {
+         output_asm_insn ("tfr\t%1,%0", operands);
+       }
+      else if (H_REG_P (operands[0]))
+       {
+         if (SP_REG_P (operands[0]))
+           output_asm_insn ("lds\t%1", operands);
+         else
+           output_asm_insn ("ld%0\t%1", operands);
+       }
+      else if (H_REG_P (operands[1]))
+       {
+         if (SP_REG_P (operands[1]))
+           output_asm_insn ("sts\t%0", operands);
+         else
+           output_asm_insn ("st%1\t%0", operands);
+       }
+      else
+       {
+         rtx from = operands[1];
+         rtx to = operands[0];
+
+         if ((m68hc11_register_indirect_p (from, GET_MODE (from))
+              && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
+             || (m68hc11_register_indirect_p (to, GET_MODE (to))
+                 && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
+           {
+             rtx ops[3];
+
+             if (operands[2])
+               {
+                 ops[0] = operands[2];
+                 ops[1] = from;
+                 ops[2] = 0;
+                 m68hc11_gen_movhi (insn, ops);
+                 ops[0] = to;
+                 ops[1] = operands[2];
+                 m68hc11_gen_movhi (insn, ops);
+               }
+             else
+               {
+                 /* !!!! SCz wrong here.  */
+               }
+           }
+         else
+           {
+             if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
+               {
+                 output_asm_insn ("clr\t%h0", operands);
+                 output_asm_insn ("clr\t%b0", operands);
+               }
+             else
+               {
+                 output_asm_insn ("movw\t%1,%0", operands);
+               }
+           }
+       }
+      return;
+    }
+
+  if (IS_STACK_POP (operands[1]) && H_REG_P (operands[0]))
+    {
+      switch (REGNO (operands[0]))
+       {
+       case HARD_X_REGNUM:
+       case HARD_Y_REGNUM:
+         output_asm_insn ("pul%0", operands);
+         break;
+       case HARD_D_REGNUM:
+         output_asm_insn ("pula", operands);
+         output_asm_insn ("pulb", operands);
+         break;
+       default:
+         abort ();
+       }
+      return;
+    }
+  /* Some moves to a hard register are special. Not all of them
+     are really supported and we have to use a temporary
+     location to provide them (either the stack of a temp var). */
+  if (H_REG_P (operands[0]))
+    {
+      switch (REGNO (operands[0]))
+       {
+       case HARD_D_REGNUM:
+         if (X_REG_P (operands[1]))
+           {
+             if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
+               {
+                 m68hc11_output_swap (insn, operands);
+               }
+             else if (next_insn_test_reg (insn, operands[0]))
+               {
+                 output_asm_insn ("stx\t%t0\n\tldd\t%t0", operands);
+               }
+             else
+               {
+                 cc_status = cc_prev_status;
+                 output_asm_insn ("pshx\n\tpula\n\tpulb", operands);
+               }
+           }
+         else if (Y_REG_P (operands[1]))
+           {
+             if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
+               {
+                 m68hc11_output_swap (insn, operands);
+               }
+             else
+               {
+                 /* %t means *ZTMP scratch register. */
+                 output_asm_insn ("sty\t%t1", operands);
+                 output_asm_insn ("ldd\t%t1", operands);
+               }
+           }
+         else if (SP_REG_P (operands[1]))
+           {
+             CC_STATUS_INIT;
+             if (ix_reg == 0)
+               create_regs_rtx ();
+             if (optimize == 0 || dead_register_here (insn, ix_reg) == 0)
+               output_asm_insn ("xgdx", operands);
+             output_asm_insn ("tsx", operands);
+             output_asm_insn ("xgdx", operands);
+           }
+         else if (IS_STACK_POP (operands[1]))
+           {
+             output_asm_insn ("pula\n\tpulb", operands);
+           }
+         else if (GET_CODE (operands[1]) == CONST_INT
+                  && INTVAL (operands[1]) == 0)
+           {
+             output_asm_insn ("clra\n\tclrb", operands);
+           }
+         else
+           {
+             output_asm_insn ("ldd\t%1", operands);
+           }
+         break;
+
+       case HARD_X_REGNUM:
+         if (D_REG_P (operands[1]))
+           {
+             if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
+               {
+                 m68hc11_output_swap (insn, operands);
+               }
+             else if (next_insn_test_reg (insn, operands[0]))
+               {
+                 output_asm_insn ("std\t%t0\n\tldx\t%t0", operands);
+               }
+             else
+               {
+                 cc_status = cc_prev_status;
+                 output_asm_insn ("pshb", operands);
+                 output_asm_insn ("psha", operands);
+                 output_asm_insn ("pulx", operands);
+               }
+           }
+         else if (Y_REG_P (operands[1]))
+           {
+             output_asm_insn ("sty\t%t1", operands);
+             output_asm_insn ("ldx\t%t1", operands);
+           }
+         else if (SP_REG_P (operands[1]))
+           {
+             /* tsx, tsy preserve the flags */
+             cc_status = cc_prev_status;
+             output_asm_insn ("tsx", operands);
+           }
+         else
+           {
+             output_asm_insn ("ldx\t%1", operands);
+           }
+         break;
+
+       case HARD_Y_REGNUM:
+         if (D_REG_P (operands[1]))
+           {
+             if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
+               {
+                 m68hc11_output_swap (insn, operands);
+               }
+             else
+               {
+                 output_asm_insn ("std\t%t1", operands);
+                 output_asm_insn ("ldy\t%t1", operands);
+               }
+           }
+         else if (X_REG_P (operands[1]))
+           {
+             output_asm_insn ("stx\t%t1", operands);
+             output_asm_insn ("ldy\t%t1", operands);
+           }
+         else if (SP_REG_P (operands[1]))
+           {
+             /* tsx, tsy preserve the flags */
+             cc_status = cc_prev_status;
+             output_asm_insn ("tsy", operands);
+           }
+         else
+           {
+             output_asm_insn ("ldy\t%1", operands);
+           }
+         break;
+
+       case HARD_SP_REGNUM:
+         if (D_REG_P (operands[1]))
+           {
+             cc_status = cc_prev_status;
+             output_asm_insn ("xgdx", operands);
+             output_asm_insn ("txs", operands);
+             output_asm_insn ("xgdx", operands);
+           }
+         else if (X_REG_P (operands[1]))
+           {
+             /* tys, txs preserve the flags */
+             cc_status = cc_prev_status;
+             output_asm_insn ("txs", operands);
+           }
+         else if (Y_REG_P (operands[1]))
+           {
+             /* tys, txs preserve the flags */
+             cc_status = cc_prev_status;
+             output_asm_insn ("tys", operands);
+           }
+         else
+           {
+             /* lds sets the flags but the des does not.  */
+             CC_STATUS_INIT;
+             output_asm_insn ("lds\t%1", operands);
+             output_asm_insn ("des", operands);
+           }
+         break;
+
+       default:
+         fatal_insn ("Invalid register in the move instruction", insn);
+         break;
+       }
+      return;
+    }
+  if (SP_REG_P (operands[1]) && REG_P (operands[0])
+      && REGNO (operands[0]) == HARD_FRAME_POINTER_REGNUM)
+    {
+      output_asm_insn ("sts\t%0", operands);
+      return;
+    }
+
+  if (IS_STACK_PUSH (operands[0]) && H_REG_P (operands[1]))
+    {
+      switch (REGNO (operands[1]))
+       {
+       case HARD_X_REGNUM:
+       case HARD_Y_REGNUM:
+         output_asm_insn ("psh%1", operands);
+         break;
+       case HARD_D_REGNUM:
+         output_asm_insn ("pshb", operands);
+         output_asm_insn ("psha", operands);
+         break;
+       default:
+         abort ();
+       }
+      return;
+    }
+
+  /* Operand 1 must be a hard register.  */
+  if (!H_REG_P (operands[1]))
+    {
+      fatal_insn ("Invalid operand in the instruction", insn);
+    }
+
+  reg = REGNO (operands[1]);
+  switch (reg)
+    {
+    case HARD_D_REGNUM:
+      output_asm_insn ("std\t%0", operands);
+      break;
+
+    case HARD_X_REGNUM:
+      output_asm_insn ("stx\t%0", operands);
+      break;
+
+    case HARD_Y_REGNUM:
+      output_asm_insn ("sty\t%0", operands);
+      break;
+
+    case HARD_SP_REGNUM:
+      if (ix_reg == 0)
+       create_regs_rtx ();
+
+      if (reg_mentioned_p (ix_reg, operands[0]))
+       {
+         output_asm_insn ("sty\t%t0", operands);
+         output_asm_insn ("tsy", operands);
+         output_asm_insn ("sty\t%0", operands);
+         output_asm_insn ("ldy\t%t0", operands);
+       }
+      else
+       {
+         output_asm_insn ("stx\t%t0", operands);
+         output_asm_insn ("tsx", operands);
+         output_asm_insn ("stx\t%0", operands);
+         output_asm_insn ("ldx\t%t0", operands);
+       }
+      CC_STATUS_INIT;
+      break;
+
+    default:
+      fatal_insn ("Invalid register in the move instruction", insn);
+      break;
+    }
+}
+
+void
+m68hc11_gen_movqi (insn, operands)
+     rtx insn;
+     rtx *operands;
+{
+  /* Move a register or memory to the same location.
+     This is possible because such insn can appear
+     in a non-optimizing mode. */
+  if (operands[0] == operands[1] || rtx_equal_p (operands[0], operands[1]))
+    {
+      cc_status = cc_prev_status;
+      return;
+    }
+
+  if (TARGET_M6812)
+    {
+
+      if (H_REG_P (operands[0]) && H_REG_P (operands[1]))
+       {
+         output_asm_insn ("tfr\t%1,%0", operands);
+       }
+      else if (H_REG_P (operands[0]))
+       {
+         if (Q_REG_P (operands[0]))
+           output_asm_insn ("lda%0\t%1", operands);
+         else if (D_REG_P (operands[0]))
+           output_asm_insn ("ldab\t%1", operands);
+         else
+           output_asm_insn ("ld%0\t%1", operands);
+       }
+      else if (H_REG_P (operands[1]))
+       {
+         if (Q_REG_P (operands[1]))
+           output_asm_insn ("sta%1\t%0", operands);
+         else if (D_REG_P (operands[1]))
+           output_asm_insn ("staa\t%0", operands);
+         else
+           output_asm_insn ("st%1\t%0", operands);
+       }
+      else
+       {
+         rtx from = operands[1];
+         rtx to = operands[0];
+
+         if ((m68hc11_register_indirect_p (from, GET_MODE (from))
+              && !m68hc11_small_indexed_indirect_p (from, GET_MODE (from)))
+             || (m68hc11_register_indirect_p (to, GET_MODE (to))
+                 && !m68hc11_small_indexed_indirect_p (to, GET_MODE (to))))
+           {
+             rtx ops[3];
+
+             if (operands[2])
+               {
+                 ops[0] = operands[2];
+                 ops[1] = from;
+                 ops[2] = 0;
+                 m68hc11_gen_movqi (insn, ops);
+                 ops[0] = to;
+                 ops[1] = operands[2];
+                 m68hc11_gen_movqi (insn, ops);
+               }
+             else
+               {
+                 /* !!!! SCz wrong here.  */
+               }
+           }
+         else
+           {
+             if (GET_CODE (from) == CONST_INT && INTVAL (from) == 0)
+               {
+                 output_asm_insn ("clr\t%b0", operands);
+               }
+             else
+               {
+                 output_asm_insn ("movb\t%1,%0", operands);
+               }
+           }
+       }
+      return;
+    }
+
+  if (H_REG_P (operands[0]))
+    {
+      switch (REGNO (operands[0]))
+       {
+       case HARD_B_REGNUM:
+       case HARD_D_REGNUM:
+         if (X_REG_P (operands[1]))
+           {
+             if (optimize && find_regno_note (insn, REG_DEAD, HARD_X_REGNUM))
+               {
+                 m68hc11_output_swap (insn, operands);
+               }
+             else
+               {
+                 output_asm_insn ("stx\t%t1", operands);
+                 output_asm_insn ("ldab\t%T0", operands);
+               }
+           }
+         else if (Y_REG_P (operands[1]))
+           {
+             if (optimize && find_regno_note (insn, REG_DEAD, HARD_Y_REGNUM))
+               {
+                 m68hc11_output_swap (insn, operands);
+               }
+             else
+               {
+                 output_asm_insn ("sty\t%t1", operands);
+                 output_asm_insn ("ldab\t%T0", operands);
+               }
+           }
+         else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
+                  && !DA_REG_P (operands[1]))
+           {
+             output_asm_insn ("ldab\t%b1", operands);
+           }
+         else if (DA_REG_P (operands[1]))
+           {
+             output_asm_insn ("tab", operands);
+           }
+         else
+           {
+             cc_status = cc_prev_status;
+             return;
+           }
+         break;
+
+       case HARD_A_REGNUM:
+         if (X_REG_P (operands[1]))
+           {
+             output_asm_insn ("stx\t%t1", operands);
+             output_asm_insn ("ldaa\t%T0", operands);
+           }
+         else if (Y_REG_P (operands[1]))
+           {
+             output_asm_insn ("sty\t%t1", operands);
+             output_asm_insn ("ldaa\t%T0", operands);
+           }
+         else if (!DB_REG_P (operands[1]) && !D_REG_P (operands[1])
+                  && !DA_REG_P (operands[1]))
+           {
+             output_asm_insn ("ldaa\t%b1", operands);
+           }
+         else if (!DA_REG_P (operands[1]))
+           {
+             output_asm_insn ("tba", operands);
+           }
+         else
+           {
+             cc_status = cc_prev_status;
+           }
+         break;
+
+       case HARD_X_REGNUM:
+         if (D_REG_P (operands[1]))
+           {
+             if (optimize && find_regno_note (insn, REG_DEAD, HARD_D_REGNUM))
+               {
+                 m68hc11_output_swap (insn, operands);
+               }
+             else
+               {
+                 output_asm_insn ("stab\t%T1", operands);
+                 output_asm_insn ("ldx\t%t1", operands);
+               }
+             CC_STATUS_INIT;
+           }
+         else if (Y_REG_P (operands[1]))
+           {
+             output_asm_insn ("sty\t%t0", operands);
+             output_asm_insn ("ldx\t%t0", operands);
+           }
+         else if (GET_CODE (operands[1]) == CONST_INT)
+           {
+             output_asm_insn ("ldx\t%1", operands);
+           }
+         else if (dead_register_here (insn, d_reg))
+           {
+             output_asm_insn ("ldab\t%b1", operands);
+             output_asm_insn ("xgdx", operands);
+           }
+         else if (!reg_mentioned_p (operands[0], operands[1]))
+           {
+             output_asm_insn ("xgdx", operands);
+             output_asm_insn ("ldab\t%b1", operands);
+             output_asm_insn ("xgdx", operands);
+           }
+         else
+           {
+             output_asm_insn ("pshb", operands);
+             output_asm_insn ("ldab\t%b1", operands);
+             output_asm_insn ("stab\t%T1", operands);
+             output_asm_insn ("ldx\t%t1", operands);
+             output_asm_insn ("pulb", operands);
+             CC_STATUS_INIT;
+           }
+         break;
+
+       case HARD_Y_REGNUM:
+         if (D_REG_P (operands[1]))
+           {
+             output_asm_insn ("stab\t%T1", operands);
+             output_asm_insn ("ldy\t%t1", operands);
+             CC_STATUS_INIT;
+           }
+         else if (X_REG_P (operands[1]))
+           {
+             output_asm_insn ("stx\t%t1", operands);
+             output_asm_insn ("ldy\t%t1", operands);
+             CC_STATUS_INIT;
+           }
+         else if (GET_CODE (operands[1]) == CONST_INT)
+           {
+             output_asm_insn ("ldy\t%1", operands);
+           }
+         else if (dead_register_here (insn, d_reg))
+           {
+             output_asm_insn ("ldab\t%b1", operands);
+             output_asm_insn ("xgdy", operands);
+           }
+         else if (!reg_mentioned_p (operands[0], operands[1]))
+           {
+             output_asm_insn ("xgdy", operands);
+             output_asm_insn ("ldab\t%b1", operands);
+             output_asm_insn ("xgdy", operands);
+           }
+         else
+           {
+             output_asm_insn ("pshb", operands);
+             output_asm_insn ("ldab\t%b1", operands);
+             output_asm_insn ("stab\t%T1", operands);
+             output_asm_insn ("ldy\t%t1", operands);
+             output_asm_insn ("pulb", operands);
+             CC_STATUS_INIT;
+           }
+         break;
+
+       default:
+         fatal_insn ("Invalid register in the instruction", insn);
+         break;
+       }
+    }
+  else if (H_REG_P (operands[1]))
+    {
+      switch (REGNO (operands[1]))
+       {
+       case HARD_D_REGNUM:
+       case HARD_B_REGNUM:
+         output_asm_insn ("stab\t%b0", operands);
+         break;
+
+       case HARD_A_REGNUM:
+         output_asm_insn ("staa\t%b0", operands);
+         break;
+
+       case HARD_X_REGNUM:
+         output_asm_insn ("xgdx\n\tstab\t%b0\n\txgdx", operands);
+         break;
+
+       case HARD_Y_REGNUM:
+         output_asm_insn ("xgdy\n\tstab\t%b0\n\txgdy", operands);
+         break;
+
+       default:
+         fatal_insn ("Invalid register in the move instruction", insn);
+         break;
+       }
+      return;
+    }
+  else
+    {
+      fatal_insn ("Operand 1 must be a hard register", insn);
+    }
+}
+
+/* Generate the code for a ROTATE or ROTATERT on a QI or HI mode.
+   The source and destination must be D or A and the shift must
+   be a constant.  */
+void
+m68hc11_gen_rotate (code, insn, operands)
+     enum rtx_code code;
+     rtx insn;
+     rtx operands[];
+{
+  int val;
+  
+  if (GET_CODE (operands[2]) != CONST_INT
+      || (!D_REG_P (operands[0]) && !DA_REG_P (operands[0])))
+    fatal_insn ("Invalid rotate insn", insn);
+
+  val = INTVAL (operands[2]);
+  if (code == ROTATERT)
+    val = GET_MODE_SIZE (GET_MODE (operands[0])) * BITS_PER_UNIT - val;
+
+  if (GET_MODE (operands[0]) != QImode)
+    CC_STATUS_INIT;
+
+  /* Rotate by 8-bits if the shift is within [5..11].  */
+  if (val >= 5 && val <= 11)
+    {
+      output_asm_insn ("psha", operands);
+      output_asm_insn ("tba", operands);
+      output_asm_insn ("pulb", operands);
+      val -= 8;
+    }
+
+  /* If the shift is big, invert the rotation.  */
+  else if (val >= 12)
+    {
+      val = val - 16;
+    }
+
+  if (val > 0)
+    {
+      /* Set the carry to bit-15, but don't change D yet.  */
+      if (GET_MODE (operands[0]) != QImode)
+        {
+          output_asm_insn ("asra", operands);
+          output_asm_insn ("rola", operands);
+        }
+
+      while (--val >= 0)
+        {
+          /* Rotate B first to move the carry to bit-0.  */
+          if (D_REG_P (operands[0]))
+            output_asm_insn ("rolb", operands);
+
+          if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
+            output_asm_insn ("rola", operands);
+        }
+    }
+  else
+    {
+      /* Set the carry to bit-8 of D.  */
+      if (val != 0 && GET_MODE (operands[0]) != QImode)
+        {
+          output_asm_insn ("tap", operands);
+        }
+      
+      while (++val <= 0)
+        {
+          /* Rotate B first to move the carry to bit-7.  */
+          if (D_REG_P (operands[0]))
+            output_asm_insn ("rorb", operands);
+
+          if (GET_MODE (operands[0]) != QImode || DA_REG_P (operands[0]))
+            output_asm_insn ("rora", operands);
+        }
+    }
+}
+
+\f
+
+/* Store in cc_status the expressions that the condition codes will
+   describe after execution of an instruction whose pattern is EXP.
+   Do not alter them if the instruction would not alter the cc's.  */
+
+void
+m68hc11_notice_update_cc (exp, insn)
+     rtx exp;
+     rtx insn ATTRIBUTE_UNUSED;
+{
+  /* recognize SET insn's.  */
+  if (GET_CODE (exp) == SET)
+    {
+      /* Jumps do not alter the cc's.  */
+      if (SET_DEST (exp) == pc_rtx)
+       ;
+
+      /* NOTE: most instructions don't affect the carry bit, but the
+         bhi/bls/bhs/blo instructions use it.  This isn't mentioned in
+         the conditions.h header.  */
+
+      /* Function calls clobber the cc's.  */
+      else if (GET_CODE (SET_SRC (exp)) == CALL)
+       {
+         CC_STATUS_INIT;
+       }
+
+      /* Tests and compares set the cc's in predictable ways.  */
+      else if (SET_DEST (exp) == cc0_rtx)
+       {
+         cc_status.flags = 0;
+         cc_status.value1 = XEXP (exp, 0);
+         cc_status.value2 = XEXP (exp, 1);
+       }
+      else
+       {
+         /* All other instructions affect the condition codes.  */
+         cc_status.flags = 0;
+         cc_status.value1 = XEXP (exp, 0);
+         cc_status.value2 = XEXP (exp, 1);
+       }
+    }
+  else
+    {
+      /* Default action if we haven't recognized something
+         and returned earlier.  */
+      CC_STATUS_INIT;
+    }
+
+  if (cc_status.value2 != 0)
+    switch (GET_CODE (cc_status.value2))
+      {
+       /* These logical operations can generate several insns.
+          The flags are setup according to what is generated.  */
+      case IOR:
+      case XOR:
+      case AND:
+       break;
+
+       /* The (not ...) generates several 'com' instructions for
+          non QImode.  We have to invalidate the flags.  */
+      case NOT:
+       if (GET_MODE (cc_status.value2) != QImode)
+         CC_STATUS_INIT;
+       break;
+
+      case PLUS:
+      case MINUS:
+      case MULT:
+      case DIV:
+      case UDIV:
+      case MOD:
+      case UMOD:
+      case NEG:
+       if (GET_MODE (cc_status.value2) != VOIDmode)
+         cc_status.flags |= CC_NO_OVERFLOW;
+       break;
+
+       /* The asl sets the overflow bit in such a way that this
+          makes the flags unusable for a next compare insn.  */
+      case ASHIFT:
+      case ROTATE:
+      case ROTATERT:
+       if (GET_MODE (cc_status.value2) != VOIDmode)
+         cc_status.flags |= CC_NO_OVERFLOW;
+       break;
+
+       /* A load/store instruction does not affect the carry.  */
+      case MEM:
+      case SYMBOL_REF:
+      case REG:
+      case CONST_INT:
+       cc_status.flags |= CC_NO_OVERFLOW;
+       break;
+
+      default:
+       break;
+      }
+  if (cc_status.value1 && GET_CODE (cc_status.value1) == REG
+      && cc_status.value2
+      && reg_overlap_mentioned_p (cc_status.value1, cc_status.value2))
+    cc_status.value2 = 0;
+}
+\f
+
+/* Machine Specific Reorg. */
+
+/* Z register replacement:
+
+   GCC treats the Z register as an index base address register like
+   X or Y.  In general, it uses it during reload to compute the address
+   of some operand.  This helps the reload pass to avoid to fall into the
+   register spill failure.
+
+   The Z register is in the A_REGS class.  In the machine description,
+   the 'A' constraint matches it.  The 'x' or 'y' constraints do not.
+
+   It can appear everywhere an X or Y register can appear, except for
+   some templates in the clobber section (when a clobber of X or Y is asked).
+   For a given instruction, the template must ensure that no more than
+   2 'A' registers are used.  Otherwise, the register replacement is not
+   possible.
+
+   To replace the Z register, the algorithm is not terrific:
+   1. Insns that do not use the Z register are not changed
+   2. When a Z register is used, we scan forward the insns to see
+   a potential register to use: either X or Y and sometimes D.
+   We stop when a call, a label or a branch is seen, or when we
+   detect that both X and Y are used (probably at different times, but it does
+   not matter).
+   3. The register that will be used for the replacement of Z is saved
+   in a .page0 register or on the stack.  If the first instruction that
+   used Z, uses Z as an input, the value is loaded from another .page0
+   register.  The replacement register is pushed on the stack in the
+   rare cases where a compare insn uses Z and we couldn't find if X/Y
+   are dead.
+   4. The Z register is replaced in all instructions until we reach
+   the end of the Z-block, as detected by step 2.
+   5. If we detect that Z is still alive, its value is saved.
+   If the replacement register is alive, its old value is loaded.
+
+   The Z register can be disabled with -ffixed-z.
+*/
+
+struct replace_info
+{
+  rtx first;
+  rtx replace_reg;
+  int need_save_z;
+  int must_load_z;
+  int must_save_reg;
+  int must_restore_reg;
+  rtx last;
+  int regno;
+  int x_used;
+  int y_used;
+  int can_use_d;
+  int found_call;
+  int z_died;
+  int z_set_count;
+  rtx z_value;
+  int must_push_reg;
+  int save_before_last;
+  int z_loaded_with_sp;
+};
+
+static rtx z_reg_qi;
+
+static int m68hc11_check_z_replacement PARAMS ((rtx, struct replace_info *));
+static void m68hc11_find_z_replacement PARAMS ((rtx, struct replace_info *));
+static void m68hc11_z_replacement PARAMS ((rtx));
+static void m68hc11_reassign_regs PARAMS ((rtx));
+
+int z_replacement_completed = 0;
+
+/* Analyze the insn to find out which replacement register to use and
+   the boundaries of the replacement.
+   Returns 0 if we reached the last insn to be replaced, 1 if we can
+   continue replacement in next insns. */
+
+static int
+m68hc11_check_z_replacement (insn, info)
+     rtx insn;
+     struct replace_info *info;
+{
+  int this_insn_uses_ix;
+  int this_insn_uses_iy;
+  int this_insn_uses_z;
+  int this_insn_uses_d;
+  rtx body;
+  int z_dies_here;
+
+  /* A call is said to clobber the Z register, we don't need
+     to save the value of Z.  We also don't need to restore
+     the replacement register (unless it is used by the call).  */
+  if (GET_CODE (insn) == CALL_INSN)
+    {
+      body = PATTERN (insn);
+
+      info->can_use_d = 0;
+
+      /* If the call is an indirect call with Z, we have to use the
+         Y register because X can be used as an input (D+X).
+         We also must not save Z nor restore Y.  */
+      if (reg_mentioned_p (z_reg, body))
+       {
+         insn = NEXT_INSN (insn);
+         info->x_used = 1;
+         info->y_used = 0;
+         info->found_call = 1;
+         info->must_restore_reg = 0;
+         info->last = NEXT_INSN (insn);
+       }
+      info->need_save_z = 0;
+      return 0;
+    }
+  if (GET_CODE (insn) == CODE_LABEL
+      || GET_CODE (insn) == BARRIER || GET_CODE (insn) == ASM_INPUT)
+    return 0;
+
+  if (GET_CODE (insn) == JUMP_INSN)
+    {
+      if (reg_mentioned_p (z_reg, insn) == 0)
+       return 0;
+
+      info->can_use_d = 0;
+      info->must_save_reg = 0;
+      info->must_restore_reg = 0;
+      info->need_save_z = 0;
+      info->last = NEXT_INSN (insn);
+      return 0;
+    }
+  if (GET_CODE (insn) != INSN && GET_CODE (insn) != JUMP_INSN)
+    {
+      return 1;
+    }
+
+  /* Z register dies here.  */
+  z_dies_here = find_regno_note (insn, REG_DEAD, HARD_Z_REGNUM) != NULL;
+
+  body = PATTERN (insn);
+  if (GET_CODE (body) == SET)
+    {
+      rtx src = XEXP (body, 1);
+      rtx dst = XEXP (body, 0);
+
+      /* Condition code is set here. We have to restore the X/Y and
+         save into Z before any test/compare insn because once we save/restore
+         we can change the condition codes. When the compare insn uses Z and
+         we can't use X/Y, the comparison is made with the *ZREG soft register
+         (this is supported by cmphi, cmpqi, tsthi, tstqi patterns).  */
+      if (dst == cc0_rtx)
+       {
+         if ((GET_CODE (src) == REG && REGNO (src) == HARD_Z_REGNUM)
+             || (GET_CODE (src) == COMPARE &&
+                 (rtx_equal_p (XEXP (src, 0), z_reg)
+                  || rtx_equal_p (XEXP (src, 1), z_reg))))
+           {
+             if (insn == info->first)
+               {
+                 info->must_load_z = 0;
+                 info->must_save_reg = 0;
+                 info->must_restore_reg = 0;
+                 info->need_save_z = 0;
+                 info->found_call = 1;
+                 info->regno = SOFT_Z_REGNUM;
+                 info->last = insn;
+               }
+             return 0;
+           }
+         if (reg_mentioned_p (z_reg, src) == 0)
+           {
+             info->can_use_d = 0;
+             return 0;
+           }
+
+         if (insn != info->first)
+           return 0;
+
+         /* Compare insn which uses Z.  We have to save/restore the X/Y
+            register without modifying the condition codes.  For this
+            we have to use a push/pop insn.  */
+         info->must_push_reg = 1;
+         info->last = insn;
+       }
+
+      /* Z reg is set to something new. We don't need to load it.  */
+      if (Z_REG_P (dst))
+       {
+         if (!reg_mentioned_p (z_reg, src))
+           {
+             if (insn == info->first)
+               {
+                 info->must_load_z = 0;
+               }
+           }
+         info->z_set_count++;
+         info->z_value = src;
+         if (SP_REG_P (src))
+           info->z_loaded_with_sp = 1;
+       }
+      else if (reg_mentioned_p (z_reg, dst))
+       info->can_use_d = 0;
+
+      this_insn_uses_d = reg_mentioned_p (d_reg, src)
+       | reg_mentioned_p (d_reg, dst);
+      this_insn_uses_ix = reg_mentioned_p (ix_reg, src)
+       | reg_mentioned_p (ix_reg, dst);
+      this_insn_uses_iy = reg_mentioned_p (iy_reg, src)
+       | reg_mentioned_p (iy_reg, dst);
+      this_insn_uses_z = reg_mentioned_p (z_reg, src);
+
+      /* If z is used as an address operand (like (MEM (reg z))),
+         we can't replace it with d.  */
+      if (this_insn_uses_z && !Z_REG_P (src))
+       info->can_use_d = 0;
+      this_insn_uses_z |= reg_mentioned_p (z_reg, dst);
+
+      if (this_insn_uses_z && this_insn_uses_ix && this_insn_uses_iy)
+       {
+         fatal_insn ("Registers IX, IY and Z used in the same INSN", insn);
+       }
+
+      if (this_insn_uses_d)
+       info->can_use_d = 0;
+
+      /* IX and IY are used at the same time, we have to restore
+         the value of the scratch register before this insn.  */
+      if (this_insn_uses_ix && this_insn_uses_iy)
+       {
+         return 0;
+       }
+
+      if (info->x_used == 0 && this_insn_uses_ix)
+       {
+         if (info->y_used)
+           {
+             /* We have a (set (REG:HI X) (REG:HI Z)).
+                Since we use Z as the replacement register, this insn
+                is no longer necessary.  We turn it into a note.  We must
+                not reload the old value of X.  */
+             if (X_REG_P (dst) && rtx_equal_p (src, z_reg))
+               {
+                 if (z_dies_here)
+                   {
+                     info->need_save_z = 0;
+                     info->z_died = 1;
+                   }
+                 info->must_save_reg = 0;
+                 info->must_restore_reg = 0;
+                 info->found_call = 1;
+                 info->can_use_d = 0;
+                 PUT_CODE (insn, NOTE);
+                 NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+                 NOTE_SOURCE_FILE (insn) = 0;
+                 info->last = NEXT_INSN (insn);
+                 return 0;
+               }
+
+             if (X_REG_P (dst)
+                 && (rtx_equal_p (src, z_reg)
+                     || (z_dies_here && !reg_mentioned_p (ix_reg, src))))
+               {
+                 if (z_dies_here)
+                   {
+                     info->need_save_z = 0;
+                     info->z_died = 1;
+                   }
+                 info->last = NEXT_INSN (insn);
+                 info->must_save_reg = 0;
+                 info->must_restore_reg = 0;
+               }
+             else if (X_REG_P (dst) && reg_mentioned_p (z_reg, src)
+                      && !reg_mentioned_p (ix_reg, src))
+               {
+                 if (z_dies_here)
+                   {
+                     info->z_died = 1;
+                     info->need_save_z = 0;
+                   }
+                 else
+                   {
+                     info->save_before_last = 1;
+                   }
+                 info->must_restore_reg = 0;
+                 info->last = NEXT_INSN (insn);
+               }
+             else if (info->can_use_d)
+               {
+                 info->last = NEXT_INSN (insn);
+                 info->x_used = 1;
+               }
+             return 0;
+           }
+         info->x_used = 1;
+         if (z_dies_here && !reg_mentioned_p (src, ix_reg)
+             && GET_CODE (src) == REG && REGNO (src) == HARD_X_REGNUM)
+           {
+             info->need_save_z = 0;
+             info->z_died = 1;
+             info->last = NEXT_INSN (insn);
+             info->regno = HARD_X_REGNUM;
+             info->must_save_reg = 0;
+             info->must_restore_reg = 0;
+             return 0;
+           }
+       }
+      if (info->y_used == 0 && this_insn_uses_iy)
+       {
+         if (info->x_used)
+           {
+             if (Y_REG_P (dst) && rtx_equal_p (src, z_reg))
+               {
+                 if (z_dies_here)
+                   {
+                     info->need_save_z = 0;
+                     info->z_died = 1;
+                   }
+                 info->must_save_reg = 0;
+                 info->must_restore_reg = 0;
+                 info->found_call = 1;
+                 info->can_use_d = 0;
+                 PUT_CODE (insn, NOTE);
+                 NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+                 NOTE_SOURCE_FILE (insn) = 0;
+                 info->last = NEXT_INSN (insn);
+                 return 0;
+               }
+
+             if (Y_REG_P (dst)
+                 && (rtx_equal_p (src, z_reg)
+                     || (z_dies_here && !reg_mentioned_p (iy_reg, src))))
+               {
+                 if (z_dies_here)
+                   {
+                     info->z_died = 1;
+                     info->need_save_z = 0;
+                   }
+                 info->last = NEXT_INSN (insn);
+                 info->must_save_reg = 0;
+                 info->must_restore_reg = 0;
+               }
+             else if (Y_REG_P (dst) && reg_mentioned_p (z_reg, src)
+                      && !reg_mentioned_p (iy_reg, src))
+               {
+                 if (z_dies_here)
+                   {
+                     info->z_died = 1;
+                     info->need_save_z = 0;
+                   }
+                 else
+                   {
+                     info->save_before_last = 1;
+                   }
+                 info->must_restore_reg = 0;
+                 info->last = NEXT_INSN (insn);
+               }
+             else if (info->can_use_d)
+               {
+                 info->last = NEXT_INSN (insn);
+                 info->y_used = 1;
+               }
+
+             return 0;
+           }
+         info->y_used = 1;
+         if (z_dies_here && !reg_mentioned_p (src, iy_reg)
+             && GET_CODE (src) == REG && REGNO (src) == HARD_Y_REGNUM)
+           {
+             info->need_save_z = 0;
+             info->z_died = 1;
+             info->last = NEXT_INSN (insn);
+             info->regno = HARD_Y_REGNUM;
+             info->must_save_reg = 0;
+             info->must_restore_reg = 0;
+             return 0;
+           }
+       }
+      if (z_dies_here)
+       {
+         info->need_save_z = 0;
+         info->z_died = 1;
+         if (info->last == 0)
+           info->last = NEXT_INSN (insn);
+         return 0;
+       }
+      return info->last != NULL_RTX ? 0 : 1;
+    }
+  if (GET_CODE (body) == PARALLEL)
+    {
+      int i;
+      char ix_clobber = 0;
+      char iy_clobber = 0;
+      char z_clobber = 0;
+      this_insn_uses_iy = 0;
+      this_insn_uses_ix = 0;
+      this_insn_uses_z = 0;
+
+      for (i = XVECLEN (body, 0) - 1; i >= 0; i--)
+       {
+         rtx x;
+         int uses_ix, uses_iy, uses_z;
+
+         x = XVECEXP (body, 0, i);
+
+         if (info->can_use_d && reg_mentioned_p (d_reg, x))
+           info->can_use_d = 0;
+
+         uses_ix = reg_mentioned_p (ix_reg, x);
+         uses_iy = reg_mentioned_p (iy_reg, x);
+         uses_z = reg_mentioned_p (z_reg, x);
+         if (GET_CODE (x) == CLOBBER)
+           {
+             ix_clobber |= uses_ix;
+             iy_clobber |= uses_iy;
+             z_clobber |= uses_z;
+           }
+         else
+           {
+             this_insn_uses_ix |= uses_ix;
+             this_insn_uses_iy |= uses_iy;
+             this_insn_uses_z |= uses_z;
+           }
+         if (uses_z && GET_CODE (x) == SET)
+           {
+             rtx dst = XEXP (x, 0);
+
+             if (Z_REG_P (dst))
+               info->z_set_count++;
+           }
+         if (z_clobber)
+           info->need_save_z = 0;
+       }
+      if (debug_m6811)
+       {
+         printf ("Uses X:%d Y:%d Z:%d CX:%d CY:%d CZ:%d\n",
+                 this_insn_uses_ix, this_insn_uses_iy,
+                 this_insn_uses_z, ix_clobber, iy_clobber, z_clobber);
+         debug_rtx (insn);
+       }
+      if (this_insn_uses_z)
+       info->can_use_d = 0;
+
+      if (z_clobber && info->first != insn)
+       {
+         info->need_save_z = 0;
+         info->last = insn;
+         return 0;
+       }
+      if (z_clobber && info->x_used == 0 && info->y_used == 0)
+       {
+         if (this_insn_uses_z == 0 && insn == info->first)
+           {
+             info->must_load_z = 0;
+           }
+         if (dead_register_here (insn, d_reg))
+           {
+             info->regno = HARD_D_REGNUM;
+             info->must_save_reg = 0;
+             info->must_restore_reg = 0;
+           }
+         else if (dead_register_here (insn, ix_reg))
+           {
+             info->regno = HARD_X_REGNUM;
+             info->must_save_reg = 0;
+             info->must_restore_reg = 0;
+           }
+         else if (dead_register_here (insn, iy_reg))
+           {
+             info->regno = HARD_Y_REGNUM;
+             info->must_save_reg = 0;
+             info->must_restore_reg = 0;
+           }
+         if (info->regno >= 0)
+           {
+             info->last = NEXT_INSN (insn);
+             return 0;
+           }
+         if (this_insn_uses_ix == 0)
+           {
+             info->regno = HARD_X_REGNUM;
+             info->must_save_reg = 1;
+             info->must_restore_reg = 1;
+           }
+         else if (this_insn_uses_iy == 0)
+           {
+             info->regno = HARD_Y_REGNUM;
+             info->must_save_reg = 1;
+             info->must_restore_reg = 1;
+           }
+         else
+           {
+             info->regno = HARD_D_REGNUM;
+             info->must_save_reg = 1;
+             info->must_restore_reg = 1;
+           }
+         info->last = NEXT_INSN (insn);
+         return 0;
+       }
+
+      if (((info->x_used || this_insn_uses_ix) && iy_clobber)
+         || ((info->y_used || this_insn_uses_iy) && ix_clobber))
+       {
+         if (this_insn_uses_z)
+           {
+             if (info->y_used == 0 && iy_clobber)
+               {
+                 info->regno = HARD_Y_REGNUM;
+                 info->must_save_reg = 0;
+                 info->must_restore_reg = 0;
+               }
+             info->last = NEXT_INSN (insn);
+             info->save_before_last = 1;
+           }
+         return 0;
+       }
+      if (this_insn_uses_ix && this_insn_uses_iy)
+       {
+          if (this_insn_uses_z)
+            {
+              fatal_insn ("Cannot do z-register replacement", insn);
+            }
+         return 0;
+       }
+      if (info->x_used == 0 && (this_insn_uses_ix || ix_clobber))
+       {
+         if (info->y_used)
+           {
+             return 0;
+           }
+         info->x_used = 1;
+         if (iy_clobber || z_clobber)
+           {
+             info->last = NEXT_INSN (insn);
+             info->save_before_last = 1;
+             return 0;
+           }
+       }
+
+      if (info->y_used == 0 && (this_insn_uses_iy || iy_clobber))
+       {
+         if (info->x_used)
+           {
+             return 0;
+           }
+         info->y_used = 1;
+         if (ix_clobber || z_clobber)
+           {
+             info->last = NEXT_INSN (insn);
+             info->save_before_last = 1;
+             return 0;
+           }
+       }
+      if (z_dies_here)
+       {
+         info->z_died = 1;
+         info->need_save_z = 0;
+       }
+      return 1;
+    }
+  if (GET_CODE (body) == CLOBBER)
+    {
+
+      /* IX and IY are used at the same time, we have to restore
+         the value of the scratch register before this insn.  */
+      if (this_insn_uses_ix && this_insn_uses_iy)
+       {
+         return 0;
+       }
+      if (info->x_used == 0 && this_insn_uses_ix)
+       {
+         if (info->y_used)
+           {
+             return 0;
+           }
+         info->x_used = 1;
+       }
+      if (info->y_used == 0 && this_insn_uses_iy)
+       {
+         if (info->x_used)
+           {
+             return 0;
+           }
+         info->y_used = 1;
+       }
+      return 1;
+    }
+  return 1;
+}
+
+static void
+m68hc11_find_z_replacement (insn, info)
+     rtx insn;
+     struct replace_info *info;
+{
+  int reg;
+
+  info->replace_reg = NULL_RTX;
+  info->must_load_z = 1;
+  info->need_save_z = 1;
+  info->must_save_reg = 1;
+  info->must_restore_reg = 1;
+  info->first = insn;
+  info->x_used = 0;
+  info->y_used = 0;
+  info->can_use_d = TARGET_M6811 ? 1 : 0;
+  info->found_call = 0;
+  info->z_died = 0;
+  info->last = 0;
+  info->regno = -1;
+  info->z_set_count = 0;
+  info->z_value = NULL_RTX;
+  info->must_push_reg = 0;
+  info->save_before_last = 0;
+  info->z_loaded_with_sp = 0;
+
+  /* Scan the insn forward to find an address register that is not used.
+     Stop when:
+     - the flow of the program changes,
+     - when we detect that both X and Y are necessary,
+     - when the Z register dies,
+     - when the condition codes are set.  */
+
+  for (; insn && info->z_died == 0; insn = NEXT_INSN (insn))
+    {
+      if (m68hc11_check_z_replacement (insn, info) == 0)
+       break;
+    }
+
+  /* May be we can use Y or X if they contain the same value as Z.
+     This happens very often after the reload.  */
+  if (info->z_set_count == 1)
+    {
+      rtx p = info->first;
+      rtx v = 0;
+
+      if (info->x_used)
+       {
+         v = find_last_value (iy_reg, &p, insn, 1);
+       }
+      else if (info->y_used)
+       {
+         v = find_last_value (ix_reg, &p, insn, 1);
+       }
+      if (v && (v != iy_reg && v != ix_reg) && rtx_equal_p (v, info->z_value))
+       {
+         if (info->x_used)
+           info->regno = HARD_Y_REGNUM;
+         else
+           info->regno = HARD_X_REGNUM;
+         info->must_load_z = 0;
+         info->must_save_reg = 0;
+         info->must_restore_reg = 0;
+         info->found_call = 1;
+       }
+    }
+  if (info->z_set_count == 0)
+    info->need_save_z = 0;
+
+  if (insn == 0)
+    info->need_save_z = 0;
+
+  if (info->last == 0)
+    info->last = insn;
+
+  if (info->regno >= 0)
+    {
+      reg = info->regno;
+      info->replace_reg = gen_rtx (REG, HImode, reg);
+    }
+  else if (info->can_use_d)
+    {
+      reg = HARD_D_REGNUM;
+      info->replace_reg = d_reg;
+    }
+  else if (info->x_used)
+    {
+      reg = HARD_Y_REGNUM;
+      info->replace_reg = iy_reg;
+    }
+  else
+    {
+      reg = HARD_X_REGNUM;
+      info->replace_reg = ix_reg;
+    }
+  info->regno = reg;
+
+  if (info->must_save_reg && info->must_restore_reg)
+    {
+      if (insn && dead_register_here (insn, info->replace_reg))
+       {
+         info->must_save_reg = 0;
+         info->must_restore_reg = 0;
+       }
+    }
+}
+
+/* The insn uses the Z register.  Find a replacement register for it
+   (either X or Y) and replace it in the insn and the next ones until
+   the flow changes or the replacement register is used.  Instructions
+   are emited before and after the Z-block to preserve the value of
+   Z and of the replacement register.  */
+
+static void
+m68hc11_z_replacement (insn)
+     rtx insn;
+{
+  rtx replace_reg_qi;
+  rtx replace_reg;
+  struct replace_info info;
+
+  /* Find trivial case where we only need to replace z with the
+     equivalent soft register.  */
+  if (GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET)
+    {
+      rtx body = PATTERN (insn);
+      rtx src = XEXP (body, 1);
+      rtx dst = XEXP (body, 0);
+
+      if (Z_REG_P (dst) && (H_REG_P (src) && !SP_REG_P (src)))
+       {
+         XEXP (body, 0) = gen_rtx (REG, GET_MODE (dst), SOFT_Z_REGNUM);
+         return;
+       }
+      else if (Z_REG_P (src)
+              && ((H_REG_P (dst) && !SP_REG_P (src)) || dst == cc0_rtx))
+       {
+         XEXP (body, 1) = gen_rtx (REG, GET_MODE (src), SOFT_Z_REGNUM);
+         return;
+       }
+      else if (D_REG_P (dst)
+              && m68hc11_arith_operator (src, GET_MODE (src))
+              && D_REG_P (XEXP (src, 0)) && Z_REG_P (XEXP (src, 1)))
+       {
+         XEXP (src, 1) = gen_rtx (REG, GET_MODE (src), SOFT_Z_REGNUM);
+         return;
+       }
+      else if (Z_REG_P (dst) && GET_CODE (src) == CONST_INT
+              && INTVAL (src) == 0)
+       {
+         XEXP (body, 0) = gen_rtx (REG, GET_MODE (dst), SOFT_Z_REGNUM);
+         return;
+       }
+    }
+
+  m68hc11_find_z_replacement (insn, &info);
+
+  replace_reg = info.replace_reg;
+  replace_reg_qi = NULL_RTX;
+
+  /* Save the X register in a .page0 location.  */
+  if (info.must_save_reg && !info.must_push_reg)
+    {
+      rtx dst;
+
+      if (info.must_push_reg && 0)
+       dst = gen_rtx (MEM, HImode,
+                      gen_rtx (PRE_DEC, HImode,
+                               gen_rtx (REG, HImode, HARD_SP_REGNUM)));
+      else
+       dst = gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM);
+
+      emit_insn_before (gen_movhi (dst,
+                                  gen_rtx (REG, HImode, info.regno)), insn);
+    }
+  if (info.must_load_z && !info.must_push_reg)
+    {
+      emit_insn_before (gen_movhi (gen_rtx (REG, HImode, info.regno),
+                                  gen_rtx (REG, HImode, SOFT_Z_REGNUM)),
+                       insn);
+    }
+
+
+  /* Replace all occurence of Z by replace_reg.
+     Stop when the last instruction to replace is reached.
+     Also stop when we detect a change in the flow (but it's not
+     necessary; just safeguard).  */
+
+  for (; insn && insn != info.last; insn = NEXT_INSN (insn))
+    {
+      rtx body;
+
+      if (GET_CODE (insn) == CODE_LABEL || GET_CODE (insn) == BARRIER)
+       break;
+
+      if (GET_CODE (insn) != INSN
+         && GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != JUMP_INSN)
+       continue;
+
+      body = PATTERN (insn);
+      if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
+         || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+       {
+         if (debug_m6811 && reg_mentioned_p (replace_reg, body))
+           {
+             printf ("Reg mentioned here...:\n");
+             fflush (stdout);
+             debug_rtx (insn);
+           }
+
+         /* Stack pointer was decremented by 2 due to the push.
+            Correct that by adding 2 to the destination.  */
+         if (info.must_push_reg
+             && info.z_loaded_with_sp && GET_CODE (body) == SET)
+           {
+             rtx src, dst;
+
+             src = SET_SRC (body);
+             dst = SET_DEST (body);
+             if (SP_REG_P (src) && Z_REG_P (dst))
+               {
+                 emit_insn_after (gen_addhi3 (dst,
+                                              dst,
+                                              gen_rtx (CONST_INT,
+                                                       VOIDmode, 2)), insn);
+               }
+           }
+
+         /* Replace any (REG:HI Z) occurrence by either X or Y.  */
+         if (!validate_replace_rtx (z_reg, replace_reg, insn))
+           {
+             INSN_CODE (insn) = -1;
+             if (!validate_replace_rtx (z_reg, replace_reg, insn))
+               fatal_insn ("Cannot do z-register replacement", insn);
+           }
+
+         /* Likewise for (REG:QI Z). */
+         if (reg_mentioned_p (z_reg, insn))
+           {
+             if (replace_reg_qi == NULL_RTX)
+               replace_reg_qi = gen_rtx (REG, QImode, REGNO (replace_reg));
+             validate_replace_rtx (z_reg_qi, replace_reg_qi, insn);
+           }
+       }
+      if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+       break;
+    }
+
+  /* Save Z before restoring the old value.  */
+  if (insn && info.need_save_z && !info.must_push_reg)
+    {
+      rtx save_pos_insn = insn;
+
+      /* If Z is clobber by the last insn, we have to save its value
+         before the last instruction.  */
+      if (info.save_before_last)
+       save_pos_insn = PREV_INSN (save_pos_insn);
+
+      emit_insn_before (gen_movhi (gen_rtx (REG, HImode, SOFT_Z_REGNUM),
+                                  gen_rtx (REG, HImode, info.regno)),
+                       save_pos_insn);
+    }
+
+  if (info.must_push_reg && info.last)
+    {
+      rtx new_body, body;
+
+      body = PATTERN (info.last);
+      new_body = gen_rtx (PARALLEL, VOIDmode,
+                         gen_rtvec (3, body,
+                                    gen_rtx (USE, VOIDmode,
+                                             replace_reg),
+                                    gen_rtx (USE, VOIDmode,
+                                             gen_rtx (REG, HImode,
+                                                      SOFT_Z_REGNUM))));
+      PATTERN (info.last) = new_body;
+
+      /* Force recognition on insn since we changed it.  */
+      INSN_CODE (insn) = -1;
+
+      if (!validate_replace_rtx (z_reg, replace_reg, info.last))
+       {
+         fatal_insn ("Invalid Z register replacement for insn", insn);
+       }
+      insn = NEXT_INSN (info.last);
+    }
+
+  /* Restore replacement register unless it was died.  */
+  if (insn && info.must_restore_reg && !info.must_push_reg)
+    {
+      rtx dst;
+
+      if (info.must_push_reg && 0)
+       dst = gen_rtx (MEM, HImode,
+                      gen_rtx (POST_INC, HImode,
+                               gen_rtx (REG, HImode, HARD_SP_REGNUM)));
+      else
+       dst = gen_rtx (REG, HImode, SOFT_SAVED_XY_REGNUM);
+
+      emit_insn_before (gen_movhi (gen_rtx (REG, HImode, info.regno),
+                                  dst), insn);
+    }
+
+}
+
+
+/* Scan all the insn and re-affects some registers
+    - The Z register (if it was used), is affected to X or Y depending
+      on the instruction.  */
+
+static void
+m68hc11_reassign_regs (first)
+     rtx first;
+{
+  rtx insn;
+
+  ix_reg = gen_rtx (REG, HImode, HARD_X_REGNUM);
+  iy_reg = gen_rtx (REG, HImode, HARD_Y_REGNUM);
+  z_reg = gen_rtx (REG, HImode, HARD_Z_REGNUM);
+  z_reg_qi = gen_rtx (REG, QImode, HARD_Z_REGNUM);
+
+  /* Scan all insns to replace Z by X or Y preserving the old value
+     of X/Y and restoring it afterward.  */
+
+  for (insn = first; insn; insn = NEXT_INSN (insn))
+    {
+      rtx body;
+
+      if (GET_CODE (insn) == CODE_LABEL
+         || GET_CODE (insn) == NOTE || GET_CODE (insn) == BARRIER)
+       continue;
+
+      if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+       continue;
+
+      body = PATTERN (insn);
+      if (GET_CODE (body) == CLOBBER || GET_CODE (body) == USE)
+       continue;
+
+      if (GET_CODE (body) == CONST_INT || GET_CODE (body) == ASM_INPUT
+         || GET_CODE (body) == ASM_OPERANDS
+         || GET_CODE (body) == UNSPEC || GET_CODE (body) == UNSPEC_VOLATILE)
+       continue;
+
+      if (GET_CODE (body) == SET || GET_CODE (body) == PARALLEL
+         || GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
+       {
+
+         /* If Z appears in this insn, replace it in the current insn
+            and the next ones until the flow changes or we have to
+            restore back the replacement register.  */
+
+         if (reg_mentioned_p (z_reg, body))
+           {
+             m68hc11_z_replacement (insn);
+           }
+       }
+      else
+       {
+         printf ("Insn not handled by Z replacement:\n");
+         fflush (stdout);
+         debug_rtx (insn);
+       }
+    }
+}
+
+#if GCC_VERSION == 2095
+/* Split all insns in the function.  If UPD_LIFE, update life info after.  */
+
+static int
+m68hc11_split_all_insns (first)
+     rtx first;
+{
+  rtx insn;
+  int split_done = 0;
+
+  for (insn = first; insn; insn = NEXT_INSN (insn))
+    {
+      rtx last;
+
+      if (INSN_DELETED_P (insn))
+       continue;
+      if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+       continue;
+
+      last = try_split (PATTERN (insn), insn, 1);
+
+      /* When not optimizing, the old insn will be still left around
+         with only the 'deleted' bit set.  Transform it into a note
+         to avoid confusion of subsequent processing.  */
+      if (INSN_DELETED_P (insn))
+       {
+         PUT_CODE (insn, NOTE);
+         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+         NOTE_SOURCE_FILE (insn) = 0;
+         split_done = 1;
+       }
+
+      if (last != insn)
+       {
+         PUT_CODE (insn, NOTE);
+         NOTE_SOURCE_FILE (insn) = 0;
+         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+         split_done = 1;
+       }
+    }
+  return split_done;
+}
+#endif /* GCC_VERSION == 2095 */
+
+void
+m68hc11_reorg (first)
+     rtx first;
+{
+  int split_done = 0;
+
+  z_replacement_completed = 0;
+  z_reg = gen_rtx (REG, HImode, HARD_Z_REGNUM);
+
+#if GCC_VERSION > 2095
+  /* Some RTX are shared at this point.  This breaks the Z register
+     replacement, unshare everything.  */
+  unshare_all_rtl_again (first);
+#endif
+
+  /* Force a split of all splitable insn.  This is necessary for the
+     Z register replacement mechanism because we end up with basic insns.  */
+#if GCC_VERSION > 2095
+  split_all_insns (0);
+  split_done = 1;
+#else
+  split_done = m68hc11_split_all_insns (first);
+#endif
+
+  z_replacement_completed = 1;
+  m68hc11_reassign_regs (first);
+
+  /* After some splitting, there are some oportunities for CSE pass.
+     This happens quite often when 32-bit or above patterns are split.  */
+  if (optimize > 0 && split_done)
+    reload_cse_regs (first);
+
+  /* Re-create the REG_DEAD notes.  These notes are used in the machine
+     description to use the best assembly directives.  */
+  if (optimize)
+    {
+#if GCC_VERSION > 2095
+      find_basic_blocks (first, max_reg_num (), 0);
+      life_analysis (first, 0, PROP_REG_INFO | PROP_DEATH_NOTES);
+#else
+      find_basic_blocks (first, max_reg_num (), 0, 1);
+      life_analysis (first, max_reg_num (), 0,
+                    1 /* SCz: dead code elim fails. Must investigate. */ );
+#endif
+    }
+
+  z_replacement_completed = 2;
+
+  /* If optimizing, then go ahead and split insns that must be
+     split after Z register replacement.  This gives more opportunities
+     for peephole (in particular for consecutives xgdx/xgdy).  */
+  if (optimize > 0)
+#if GCC_VERSION > 2095
+    split_all_insns (0);
+#else
+    m68hc11_split_all_insns (first);
+#endif
+
+  /* Once insns are split after the z_replacement_completed == 2,
+     we must not re-run the life_analysis.  The xgdx/xgdy patterns
+     are not recognized and the life_analysis pass removes some
+     insns because it thinks some (SETs) are noops or made to dead
+     stores (which is false due to the swap).
+
+     Do a simple pass to eliminate the noop set that the final
+     split could generate (because it was easier for split definition).  */
+  {
+    rtx insn;
+
+    for (insn = first; insn; insn = NEXT_INSN (insn))
+      {
+       rtx body;
+
+       if (INSN_DELETED_P (insn))
+         continue;
+       if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+         continue;
+
+       /* Remove the (set (R) (R)) insns generated by some splits.  */
+       body = PATTERN (insn);
+       if (GET_CODE (body) == SET
+           && rtx_equal_p (SET_SRC (body), SET_DEST (body)))
+         {
+           PUT_CODE (insn, NOTE);
+           NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+           NOTE_SOURCE_FILE (insn) = 0;
+           continue;
+         }
+      }
+  }
+}
+\f
+
+/* Cost functions.  */
+
+#define COSTS_N_INSNS(N) ((N) * 4 - 2)
+
+/* Cost of moving memory. */
+int
+m68hc11_memory_move_cost (mode, class, in)
+     enum machine_mode mode;
+     enum reg_class class;
+     int in ATTRIBUTE_UNUSED;
+{
+  if (class <= H_REGS)
+    {
+      if (GET_MODE_SIZE (mode) <= 2)
+       return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
+      else
+       return COSTS_N_INSNS (2) + (reload_completed | reload_in_progress);
+    }
+  else
+    {
+      if (GET_MODE_SIZE (mode) <= 2)
+       return COSTS_N_INSNS (2);
+      else
+       return COSTS_N_INSNS (4);
+    }
+}
+
+
+/* Cost of moving data from a register of class 'from' to on in class 'to'.
+   Reload does not check the constraint of set insns when the two registers
+   have a move cost of 2.  Setting a higher cost will force reload to check
+   the constraints.  */
+int
+m68hc11_register_move_cost (from, to)
+     enum reg_class from;
+     enum reg_class to;
+{
+  if (from >= S_REGS && to >= S_REGS)
+    {
+      return COSTS_N_INSNS (3);
+    }
+  if (from <= S_REGS && to <= S_REGS)
+    {
+      return COSTS_N_INSNS (1) + (reload_completed | reload_in_progress);
+    }
+  return COSTS_N_INSNS (2);
+}
+
+
+/* Provide the costs of an addressing mode that contains ADDR.
+   If ADDR is not a valid address, its cost is irrelevant.  */
+
+int
+m68hc11_address_cost (addr)
+     rtx addr;
+{
+  int cost = 4;
+
+  switch (GET_CODE (addr))
+    {
+    case REG:
+      /* Make the cost of hard registers and specially SP, FP small. */
+      if (REGNO (addr) < FIRST_PSEUDO_REGISTER)
+       cost = 0;
+      else
+       cost = 1;
+      break;
+
+    case SYMBOL_REF:
+      cost = 8;
+      break;
+
+    case LABEL_REF:
+    case CONST:
+      cost = 0;
+      break;
+
+    case PLUS:
+      {
+       register rtx plus0 = XEXP (addr, 0);
+       register rtx plus1 = XEXP (addr, 1);
+
+       if (GET_CODE (plus0) != REG)
+         break;
+
+       switch (GET_CODE (plus1))
+         {
+         case CONST_INT:
+           if (INTVAL (plus1) >= 2 * m68hc11_max_offset
+               || INTVAL (plus1) < m68hc11_min_offset)
+             cost = 3;
+           else if (INTVAL (plus1) >= m68hc11_max_offset)
+             cost = 2;
+           else
+             cost = 0;
+           if (REGNO (plus0) < FIRST_PSEUDO_REGISTER)
+             cost += 0;
+           else
+             cost += 1;
+           break;
+
+         case SYMBOL_REF:
+           cost = 8;
+           break;
+
+         case CONST:
+         case LABEL_REF:
+           cost = 0;
+           break;
+
+         default:
+           break;
+         }
+       break;
+      }
+    case PRE_DEC:
+    case PRE_INC:
+      if (SP_REG_P (XEXP (addr, 0)))
+       cost = 1;
+      break;
+
+    default:
+      break;
+    }
+  if (debug_m6811)
+    {
+      printf ("Address cost: %d for :", cost);
+      fflush (stdout);
+      debug_rtx (addr);
+    }
+
+  return cost;
+}
+
+int
+m68hc11_rtx_costs (x, code, outer_code)
+     rtx x;
+     enum rtx_code code, outer_code;
+{
+  enum machine_mode mode = GET_MODE (x);
+  int extra_cost = 0;
+  int total;
+
+  switch (code)
+    {
+    case MEM:
+      return m68hc11_address_cost (XEXP (x, 0)) + 4;
+
+    case ROTATE:
+    case ROTATERT:
+    case ASHIFT:
+    case LSHIFTRT:
+    case ASHIFTRT:
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         int val = INTVAL (XEXP (x, 1));
+         int cost;
+
+         /* 8 or 16 shift instructions are fast.
+            Others are proportional to the shift counter.  */
+         if (val == 8 || val == 16 || val == -8 || val == -16)
+           {
+             val = 0;
+           }
+         cost = COSTS_N_INSNS (val + 1);
+         cost += rtx_cost (XEXP (x, 0), outer_code);
+         if (GET_MODE_SIZE (mode) >= 4 && val)
+           {
+             cost *= 4;
+           }
+         return cost;
+       }
+      total = rtx_cost (XEXP (x, 0), outer_code);
+      if (GET_MODE_SIZE (mode) >= 4)
+       {
+         total += COSTS_N_INSNS (16);
+       }
+      else
+       {
+         total += COSTS_N_INSNS (8);
+       }
+      return total;
+
+    case MINUS:
+    case PLUS:
+    case AND:
+    case XOR:
+    case IOR:
+      extra_cost = 0;
+
+      total = rtx_cost (XEXP (x, 0), outer_code)
+       + rtx_cost (XEXP (x, 1), outer_code);
+      if (GET_MODE_SIZE (mode) <= 2)
+       {
+         total += COSTS_N_INSNS (2);
+       }
+      else
+       {
+         total += COSTS_N_INSNS (4);
+       }
+      return total;
+
+    case DIV:
+    case MOD:
+      if (mode == QImode || mode == HImode)
+       {
+         return 30;
+       }
+      else if (mode == SImode)
+       {
+         return 100;
+       }
+      else
+       {
+         return 150;
+       }
+
+    case MULT:
+      if (mode == QImode)
+       {
+         return TARGET_OP_TIME ? 10 : 2;
+       }
+      if (mode == HImode)
+       {
+         return TARGET_OP_TIME ? 30 : 4;
+       }
+      if (mode == SImode)
+       {
+         return TARGET_OP_TIME ? 100 : 20;
+       }
+      return 150;
+
+    case NEG:
+    case SIGN_EXTEND:
+      extra_cost = COSTS_N_INSNS (2);
+
+      /* Fall through */
+    case NOT:
+    case COMPARE:
+    case ABS:
+    case ZERO_EXTEND:
+      total = rtx_cost (XEXP (x, 0), outer_code);
+      if (mode == QImode)
+       {
+         return total + extra_cost + COSTS_N_INSNS (1);
+       }
+      if (mode == HImode)
+       {
+         return total + extra_cost + COSTS_N_INSNS (2);
+       }
+      if (mode == SImode)
+       {
+         return total + extra_cost + COSTS_N_INSNS (4);
+       }
+      return total + extra_cost + COSTS_N_INSNS (8);
+
+    case IF_THEN_ELSE:
+      if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
+       return COSTS_N_INSNS (1);
+
+      return COSTS_N_INSNS (1);
+
+    default:
+      return COSTS_N_INSNS (4);
+    }
+}
+\f
+
+/* print_options - called at the start of the code generation for a
+   module. */
+
+#if GCC_VERSION == 2095
+extern char *main_input_filename;
+#endif
+extern char *asm_file_name;
+
+#include <time.h>
+#include <sys/types.h>
+
+static void
+print_options (out)
+     FILE *out;
+{
+  char *a_time;
+  long c_time;
+  int i;
+  extern int save_argc;
+  extern char **save_argv;
+
+  fprintf (out, ";;; Command:\t");
+  for (i = 0; i < save_argc; i++)
+    {
+      fprintf (out, "%s", save_argv[i]);
+      if (i + 1 < save_argc)
+       fprintf (out, " ");
+    }
+  fprintf (out, "\n");
+  c_time = time (0);
+  a_time = ctime (&c_time);
+  fprintf (out, ";;; Compiled:\t%s", a_time);
+#ifdef __GNUC__
+#ifndef __VERSION__
+#define __VERSION__ "[unknown]"
+#endif
+  fprintf (out, ";;; (META)compiled by GNU C version %s.\n", __VERSION__);
+#else
+  fprintf (out, ";;; (META)compiled by CC.\n");
+#endif
+}
+
+void
+m68hc11_asm_file_start (out, main_file)
+     FILE *out;
+     char *main_file;
+{
+  fprintf (out, ";;;-----------------------------------------\n");
+  fprintf (out, ";;; Start MC68HC11 gcc assembly output\n");
+  fprintf (out, ";;; gcc compiler %s\n", version_string);
+  print_options (out);
+  fprintf (out, ";;;-----------------------------------------\n");
+  output_file_directive (out, main_file);
+}
+
+
+static void
+m68hc11_add_gc_roots ()
+{
+#if GCC_VERSION > 2095
+  ggc_add_rtx_root (&m68hc11_soft_tmp_reg, 1);
+  ggc_add_rtx_root (&ix_reg, 1);
+  ggc_add_rtx_root (&iy_reg, 1);
+  ggc_add_rtx_root (&d_reg, 1);
+  ggc_add_rtx_root (&da_reg, 1);
+  ggc_add_rtx_root (&z_reg, 1);
+  ggc_add_rtx_root (&z_reg_qi, 1);
+  ggc_add_rtx_root (&stack_push_word, 1);
+  ggc_add_rtx_root (&stack_pop_word, 1);
+#endif
+}
diff --git a/gcc/config/m68hc11/m68hc11.h b/gcc/config/m68hc11/m68hc11.h
new file mode 100644 (file)
index 0000000..f802b20
--- /dev/null
@@ -0,0 +1,1895 @@
+/* Definitions of target machine for GNU compiler.
+   Motorola 68HC11 and 68HC12.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Contributed by Stephane Carrez (stcarrez@worldnet.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
+
+*/
+
+#undef GCC_VERSION
+#if 1 /* def N_*/
+# define GCC_VERSION 2096
+#else
+# define GCC_VERSION 2095
+
+/* NLS support in 2.96 */
+# define N_(X) X
+#endif
+
+#include "elfos.h"
+
+/*****************************************************************************
+**
+** Controlling the Compilation Driver, `gcc'
+**
+*****************************************************************************/
+
+#undef ENDFILE_SPEC
+
+/* Compile and assemble for a 68hc11 unless there is a -m68hc12 option.  */
+#ifndef ASM_SPEC
+#define ASM_SPEC       "%{m68hc12:-m68hc12}%{!m68hc12:-m68hc11}"
+#endif
+
+/* We need to tell the linker the target elf format.  Just pass an
+   emulation option.  This can be overriden by -Wl option of gcc.  */
+#ifndef LINK_SPEC
+#define LINK_SPEC      "%{m68hc12:-m m68hc12elf}%{!m68hc12:-m m68hc11elf}"
+#endif
+
+#ifndef LIB_SPEC
+#define LIB_SPEC       ""
+#endif
+
+#ifndef CC1_SPEC
+#define CC1_SPEC       ""
+#endif
+
+#ifndef CPP_SPEC
+#define CPP_SPEC  \
+"%{mshort:-D__HAVE_SHORT_INT__ -D__INT__=16 -D__INT_MAX__=32767}\
+ %{!mshort:-D__INT__=32 -D__INT_MAX__=2147483647}\
+ %{m68hc12:-Dmc6812 -DMC6812 -Dmc68hc12}\
+ %{!m68hc12:-Dmc6811 -DMC6811 -Dmc68hc11}"
+#endif
+
+#undef STARTFILE_SPEC
+#define STARTFILE_SPEC "crt1%O%s"
+
+/* Names to predefine in the preprocessor for this target machine.  */
+#define CPP_PREDEFINES         "-Dmc68hc1x"
+
+
+#ifndef IN_LIBGCC2
+#  include <stdio.h>
+#endif
+
+#include "gansidecl.h"
+
+#if GCC_VERSION == 2095
+#ifndef PARAMS
+#if defined(ANSI_PROTOTYPES) || defined(__cplusplus)
+#define PARAMS(args) args
+#else
+#define PARAMS(args) ()
+#endif
+#endif
+
+/* Forward type declaration for prototypes definitions.
+   rtx_ptr is equivalent to rtx. Can't use the same name. */
+struct rtx_def;
+typedef struct rtx_def *rtx_ptr;
+
+union tree_node;
+typedef union tree_node *tree_ptr;
+
+/* We can't declare enum machine_mode forward nor include 'machmode.h' here.
+   Prototypes defined here will use an int instead. It's better than no
+   prototype at all. */
+
+typedef int enum_machine_mode;
+#endif
+
+/*****************************************************************************
+**
+** Run-time Target Specification
+**
+*****************************************************************************/
+
+/* Run-time compilation parameters selecting different hardware subsets.  */
+
+extern int target_flags;
+
+extern short *reg_renumber;    /* def in local_alloc.c */
+
+/* Macros used in the machine description to test the flags.  */
+
+/* 6811 specific options
+ *
+ * For 68HC12, the auto inc/dec mode is disabled by default. The reason
+ * is that for most programs, the reload pass will fail because it needs
+ * more registers to save the value of the indexed register after the
+ * memory access.  For simple programs, you can enable this
+ * with -mauto-incdec.
+ */
+
+#define MASK_SHORT              0002   /* Compile with 16-bit `int' */
+#define MASK_AUTO_INC_DEC       0004
+#define MASK_M6811              0010
+#define MASK_M6812              0020
+
+#define TARGET_OP_TIME         (optimize && optimize_size == 0)
+#define TARGET_SHORT            (target_flags & MASK_SHORT)
+#define TARGET_M6811            (target_flags & MASK_M6811)
+#define TARGET_M6812            (target_flags & MASK_M6812)
+#define TARGET_AUTO_INC_DEC     (target_flags & MASK_AUTO_INC_DEC)
+
+/* Default target_flags if no switches specified.  */
+#ifndef TARGET_DEFAULT
+# define TARGET_DEFAULT                (MASK_M6811)
+#endif
+
+/* Define this macro as a C expression for the initializer of an
+   array of string to tell the driver program which options are
+   defaults for this target and thus do not need to be handled
+   specially when using `MULTILIB_OPTIONS'.  */
+#ifndef MULTILIB_DEFAULTS
+# if TARGET_DEFAULT & MASK_M6811
+#  define MULTILIB_DEFAULTS { "m68hc11" }
+# else
+#  define MULTILIB_DEFAULTS { "m68hc12" }
+# endif
+#endif
+
+/* Macro to define tables used to set the flags. This is a list in braces of
+   pairs in braces, each pair being { "NAME", VALUE } where VALUE is the bits
+   to set or minus the bits to clear. An empty string NAME is used to
+   identify the default VALUE.  */
+
+#define TARGET_SWITCHES                                                \
+{ { "short", MASK_SHORT,                                       \
+    N_("Compile with 16-bit integer mode")},                   \
+  { "noshort", - MASK_SHORT,                                   \
+    N_("Compile with 32-bit integer mode")},                   \
+  { "auto-incdec", MASK_AUTO_INC_DEC,                          \
+    N_("Auto pre/post decrement increment allowed")},          \
+  { "noauto-incdec", - MASK_AUTO_INC_DEC,                      \
+    N_("Auto pre/post decrement increment not allowed")},      \
+  { "68hc11", MASK_M6811,                                      \
+    N_("Compile for a 68HC11")},                               \
+  { "68hc12", MASK_M6812,                                      \
+    N_("Compile for a 68HC12")},                               \
+  { "6811",   MASK_M6811,                                      \
+    N_("Compile for a 68HC11")},                               \
+  { "6812",   MASK_M6812,                                      \
+    N_("Compile for a 68HC12")},                               \
+  { "", TARGET_DEFAULT, 0 }}
+
+/* This macro is similar to `TARGET_SWITCHES' but defines names of
+   command options that have values.  Its definition is an
+   initializer with a subgrouping for each command option.
+
+   Each subgrouping contains a string constant, that defines the
+   fixed part of the option name, and the address of a variable.  The
+   variable, type `char *', is set to the variable part of the given
+   option if the fixed part matches.  The actual option name is made
+   by appending `-m' to the specified name.  */
+#define TARGET_OPTIONS                                                 \
+{ { "reg-alloc=",      &m68hc11_reg_alloc_order,                       \
+    N_("Specify the register allocation order")},                      \
+  { "soft-reg-count=", &m68hc11_soft_reg_count,                        \
+    N_("Indicate the number of soft registers available") },           \
+  SUBTARGET_OPTIONS                                                    \
+}
+
+/* These are meant to be redefined in the host dependent files */
+#define SUBTARGET_SWITCHES
+#define SUBTARGET_OPTIONS
+
+extern const char *m68hc11_regparm_string;
+extern const char *m68hc11_reg_alloc_order;
+extern const char *m68hc11_soft_reg_count;
+
+#ifndef TARGET_M68HC12
+# define TARGET_M68HC11 1
+#endif
+
+/* Print subsidiary information on the compiler version in use.  */
+#define TARGET_VERSION         fprintf (stderr, " (MC68HC11/MC68HC12)")
+
+/* Sometimes certain combinations of command options do not make
+   sense on a particular target machine.  You can define a macro
+   `OVERRIDE_OPTIONS' to take account of this.  This macro, if
+   defined, is executed once just after all the command options have
+   been parsed.
+
+   Don't use this macro to turn on various extra optimizations for
+   `-O'.  That is what `OPTIMIZATION_OPTIONS' is for.  */
+
+#define OVERRIDE_OPTIONS       m68hc11_override_options ();
+\f
+
+/* target machine storage layout */
+
+/* Define this if most significant byte of a word is the lowest numbered.  */
+#define BYTES_BIG_ENDIAN       1
+
+/* Define this if most significant bit is lowest numbered
+   in instructions that operate on numbered bit-fields. */
+#define BITS_BIG_ENDIAN         0
+
+/* Define this if most significant word of a multiword number is numbered.  */
+#define WORDS_BIG_ENDIAN       1
+
+/* Number of bits in an addressible storage unit */
+#define BITS_PER_UNIT          8
+
+/* Number of bits in a word */
+#define BITS_PER_WORD          16
+
+/* Width of a word, in units (bytes).  */
+#define UNITS_PER_WORD         (BITS_PER_WORD/8)
+
+/* Define if you don't want extended real, but do want to use the
+   software floating point emulator for REAL_ARITHMETIC and
+   decimal <-> binary conversion.  */
+#define REAL_ARITHMETIC
+
+/* Width in bits of a pointer.  See also the macro `Pmode' defined below.  */
+#define POINTER_SIZE           16
+
+/* Definition of size_t.  This is really an unsigned short as the
+   68hc11 only handles a 64K address space.  */
+#define SIZE_TYPE               "short unsigned int"
+
+/* A C expression for a string describing the name of the data type
+   to use for the result of subtracting two pointers.  The typedef
+   name `ptrdiff_t' is defined using the contents of the string.
+   The 68hc11 only has a 64K address space.  */
+#define PTRDIFF_TYPE            "short int"
+
+/* Allocation boundary (bits) for storing pointers in memory.  */
+#define POINTER_BOUNDARY       8
+
+/* Normal alignment required for function parameters on the stack, in bits.
+   This can't be less than BITS_PER_WORD */
+#define PARM_BOUNDARY          (BITS_PER_WORD)
+
+/* Boundary (bits) on which stack pointer should be aligned.  */
+#define STACK_BOUNDARY         8
+
+/* Allocation boundary (bits) for the code of a function.  */
+#define FUNCTION_BOUNDARY      8
+
+/* Biggest alignment that any data type can require on this machine,
+   in bits. */
+#define BIGGEST_ALIGNMENT      8
+
+/* Alignment of field after `int : 0' in a structure.  */
+#define EMPTY_FIELD_BOUNDARY   8
+
+/* Every structure's size must be a multiple of this.  */
+#define STRUCTURE_SIZE_BOUNDARY 8
+
+/* Define this if instructions will fail to work if given data not
+   on the nominal alignment.  If instructions will merely go slower
+   in that case, do not define this macro.  */
+#define STRICT_ALIGNMENT       0
+
+/* An integer expression for the size in bits of the largest integer
+   machine mode that should actually be used.  All integer machine modes of
+   this size or smaller can be used for structures and unions with the
+   appropriate sizes.  */
+#define MAX_FIXED_MODE_SIZE    64
+
+/* Floats are checked in a generic way. */
+/* #define CHECK_FLOAT_VALUE(MODE, D, OVERFLOW) */
+
+
+\f
+/* target machine storage layout */
+
+/* Size (bits) of the type "int" on target machine
+   (If undefined, default is BITS_PER_WORD).  */
+#define INT_TYPE_SIZE           (TARGET_SHORT ? 16 : 32)
+
+/* Size (bits) of the type "short" on target machine */
+#define SHORT_TYPE_SIZE                16
+
+/* Size (bits) of the type "long" on target machine */
+#define LONG_TYPE_SIZE         32
+
+/* Size (bits) of the type "long long" on target machine */
+#define LONG_LONG_TYPE_SIZE     64
+
+/* Size (bits) of the type "char" on target machine */
+#define CHAR_TYPE_SIZE         8
+
+/* A C expression for the size in bits of the type `float' on the
+   target machine. If you don't define this, the default is one word.
+   Don't use default: a word is only 16.  */
+#define FLOAT_TYPE_SIZE         32
+
+/* A C expression for the size in bits of the type double on the target
+   machine. If you don't define this, the default is two words.
+   Be IEEE compliant.  */
+#define DOUBLE_TYPE_SIZE        64
+
+#define LONG_DOUBLE_TYPE_SIZE   64
+
+/* Define this as 1 if `char' should by default be signed; else as 0.  */
+#define DEFAULT_SIGNED_CHAR    0
+
+/* Define these to avoid dependence on meaning of `int'.
+   Note that WCHAR_TYPE_SIZE is used in cexp.y,
+   where TARGET_SHORT is not available.  */
+#define WCHAR_TYPE              "short int"
+#define WCHAR_TYPE_SIZE         16
+
+/* Define results of standard character escape sequences.  */
+#define TARGET_BELL            007
+#define TARGET_BS              010
+#define TARGET_TAB             011
+#define TARGET_NEWLINE         012
+#define TARGET_VT              013
+#define TARGET_FF              014
+#define TARGET_CR              015
+\f
+
+/* Standard register usage.  */
+
+#define HARD_REG_SIZE           (UNITS_PER_WORD)
+
+/* Assign names to real MC68HC11 registers.
+   A and B registers are not really used (A+B = D)
+   X register is first so that GCC allocates X+D for 32-bit integers and
+   the lowpart of that integer will be D.  Having the lower part in D is
+   better for 32<->16bit conversions and for many arithmetic operations.  */
+#define HARD_X_REGNUM          0
+#define HARD_D_REGNUM          1
+#define HARD_Y_REGNUM          2
+#define HARD_SP_REGNUM         3
+#define HARD_PC_REGNUM         4
+#define HARD_A_REGNUM          5
+#define HARD_B_REGNUM          6
+#define HARD_CCR_REGNUM                7
+
+/* The Z register does not really exist in the 68HC11.  This a fake register
+   for GCC.  It is treated exactly as an index register (X or Y).  It is only
+   in the A_REGS class, which is the BASE_REG_CLASS for GCC.  Defining this
+   register helps the reload pass of GCC.  Otherwise, the reload often aborts
+   with register spill failures.
+
+   The Z register is replaced by either X or Y during the machine specific
+   reorg (m68hc11_reorg).  It is saved in the SOFT_Z_REGNUM soft-register
+   when this is necessary.
+
+   It's possible to tell GCC not to use this register with -ffixed-z.  */
+#define HARD_Z_REGNUM           8
+
+/* The frame pointer is a soft-register.  It's treated as such by GCC:
+   it is not and must not be part of the BASE_REG_CLASS.  */
+#define DEFAULT_HARD_FP_REGNUM  (9)
+#define HARD_FP_REGNUM         (9)
+#define HARD_AP_REGNUM         (HARD_FP_REGNUM)
+
+/* Temporary soft-register used in some cases when an operand came
+   up into a bad register class (D, X, Y, SP) and gcc failed to
+   recognize this. This register is never allocated by GCC.  */
+#define SOFT_TMP_REGNUM          10
+
+/* The soft-register which is used to save the Z register
+   (see Z register replacement notes in m68hc11.c).  */
+#define SOFT_Z_REGNUM            11
+
+/* The soft-register which is used to save either X or Y. */
+#define SOFT_SAVED_XY_REGNUM     12
+
+/* A fake clobber register for 68HC12 patterns.  */
+#define FAKE_CLOBBER_REGNUM     (13)
+
+/* Define 32 soft-registers of 16-bit each.  By default,
+   only 12 of them are enabled and can be used by GCC.  The
+   -msoft-reg-count=<n> option allows to control the number of valid
+   soft-registers. GCC can put 32-bit values in them
+   by allocating consecutive registers.  The first 3 soft-registers
+   are never allocated by GCC.  They are used in case the insn template needs
+   a temporary register, or for the Z register replacement.  */
+
+#define MAX_SOFT_REG_COUNT      (32)
+#define SOFT_REG_FIXED          0, 0, 0, 0, 0, 0, 0, 0, \
+                               0, 0, 0, 0, 1, 1, 1, 1, \
+                               1, 1, 1, 1, 1, 1, 1, 1, \
+                               1, 1, 1, 1, 1, 1, 1, 1
+#define SOFT_REG_USED           0, 0, 0, 0, 0, 0, 0, 0, \
+                               0, 0, 0, 0, 1, 1, 1, 1, \
+                               1, 1, 1, 1, 1, 1, 1, 1, \
+                               1, 1, 1, 1, 1, 1, 1, 1
+#define SOFT_REG_ORDER         \
+SOFT_REG_FIRST, SOFT_REG_FIRST+1,SOFT_REG_FIRST+2,SOFT_REG_FIRST+3,\
+SOFT_REG_FIRST+4, SOFT_REG_FIRST+5,SOFT_REG_FIRST+6,SOFT_REG_FIRST+7,\
+SOFT_REG_FIRST+8, SOFT_REG_FIRST+9,SOFT_REG_FIRST+10,SOFT_REG_FIRST+11,\
+SOFT_REG_FIRST+12, SOFT_REG_FIRST+13,SOFT_REG_FIRST+14,SOFT_REG_FIRST+15,\
+SOFT_REG_FIRST+16, SOFT_REG_FIRST+17,SOFT_REG_FIRST+18,SOFT_REG_FIRST+19,\
+SOFT_REG_FIRST+20, SOFT_REG_FIRST+21,SOFT_REG_FIRST+22,SOFT_REG_FIRST+23,\
+SOFT_REG_FIRST+24, SOFT_REG_FIRST+25,SOFT_REG_FIRST+26,SOFT_REG_FIRST+27,\
+SOFT_REG_FIRST+28, SOFT_REG_FIRST+29,SOFT_REG_FIRST+30,SOFT_REG_FIRST+31
+
+#define SOFT_REG_NAMES                                                 \
+"*_.d1",  "*_.d2",  "*_.d3",  "*_.d4", \
+"*_.d5",  "*_.d6",  "*_.d7",  "*_.d8", \
+"*_.d9",  "*_.d10", "*_.d11", "*_.d12", \
+"*_.d13", "*_.d14", "*_.d15", "*_.d16",        \
+"*_.d17", "*_.d18", "*_.d19", "*_.d20", \
+"*_.d21", "*_.d22", "*_.d23", "*_.d24", \
+"*_.d25", "*_.d26", "*_.d27", "*_.d28", \
+"*_.d29", "*_.d30", "*_.d31", "*_.d32"
+
+/* First available soft-register for GCC. */
+#define SOFT_REG_FIRST          (SOFT_SAVED_XY_REGNUM+2)
+
+/* Last available soft-register for GCC. */
+#define SOFT_REG_LAST           (SOFT_REG_FIRST+MAX_SOFT_REG_COUNT)
+#define SOFT_FP_REGNUM         (SOFT_REG_LAST)
+#define SOFT_AP_REGNUM         (SOFT_FP_REGNUM+1)
+
+/* Number of actual hardware registers. The hardware registers are assigned
+   numbers for the compiler from 0 to just below FIRST_PSEUDO_REGISTER. 
+   All registers that the compiler knows about must be given numbers, even
+   those that are not normally considered general registers. */
+#define FIRST_PSEUDO_REGISTER  (SOFT_REG_LAST+2)
+
+/* 1 for registers that have pervasive standard uses and are not available
+   for the register allocator. */
+#define FIXED_REGISTERS \
+  {0, 0, 0, 1, 1, 1, 1, 1,   0, 1,  1,   1,1, 1, SOFT_REG_FIXED, 1, 1}
+/* X, D, Y, SP,PC,A, B, CCR, Z, FP,ZTMP,ZR,XYR, FK, D1 - D32, SOFT-FP, AP */
+
+/* 1 for registers not available across function calls. For our pseudo
+   registers, all are available.  */
+#define CALL_USED_REGISTERS \
+  {1, 1, 1, 1, 1, 1, 1, 1,   1, 1,  1,   1,1, 1, SOFT_REG_USED, 1, 1}
+/* X, D, Y, SP,PC,A, B, CCR, Z, FP, ZTMP,ZR,XYR, D1 - 32,     SOFT-FP, AP */
+
+
+/* Define this macro to change register usage conditional on target flags.
+
+   The soft-registers are disabled or enabled according to the
+  -msoft-reg-count=<n> option. */
+
+
+#define CONDITIONAL_REGISTER_USAGE (m68hc11_conditional_register_usage ())
+
+/* List the order in which to allocate registers.  Each register must be
+   listed once, even those in FIXED_REGISTERS.  */
+#define REG_ALLOC_ORDER                                                        \
+{ HARD_D_REGNUM, HARD_X_REGNUM, HARD_Y_REGNUM,                         \
+  SOFT_REG_ORDER, HARD_Z_REGNUM, HARD_PC_REGNUM, HARD_A_REGNUM,                \
+  HARD_B_REGNUM, HARD_CCR_REGNUM, HARD_FP_REGNUM, SOFT_FP_REGNUM,      \
+  HARD_SP_REGNUM, SOFT_TMP_REGNUM, SOFT_Z_REGNUM, SOFT_SAVED_XY_REGNUM, \
+  SOFT_AP_REGNUM, FAKE_CLOBBER_REGNUM  }
+
+/* A C expression for the number of consecutive hard registers,
+   starting at register number REGNO, required to hold a value of
+   mode MODE.  */
+#define HARD_REGNO_NREGS(REGNO, MODE) \
+((Q_REGNO_P (REGNO)) ? (GET_MODE_SIZE (MODE)) : \
+   ((GET_MODE_SIZE (MODE) + HARD_REG_SIZE - 1) / HARD_REG_SIZE))
+
+/* Value is 1 if hard register REGNO can hold a value of machine-mode MODE.
+    - 8 bit values are stored anywhere (except the SP register).
+    - 16 bit values can be stored in any register whose mode is 16
+    - 32 bit values can be stored in D, X registers or in a soft register
+      (except the last one because we need 2 soft registers)
+    - Values whose size is > 32 bit are not stored in real hard
+      registers.  They may be stored in soft registers if there are
+      enough of them.  */
+#define HARD_REGNO_MODE_OK(REGNO, MODE) \
+     hard_regno_mode_ok (REGNO,MODE)
+
+/* Value is 1 if it is a good idea to tie two pseudo registers when one has
+   mode MODE1 and one has mode MODE2.  If HARD_REGNO_MODE_OK could produce
+   different values for MODE1 and MODE2, for any hard reg, then this must be
+   0 for correct output.  */
+#define MODES_TIEABLE_P(MODE1, MODE2)                   \
+     (((MODE1) == (MODE2))                              \
+      || ((MODE1) == SImode && (MODE2) == HImode)      \
+      || ((MODE1) == HImode && (MODE2) == SImode))
+\f
+
+/* Define the classes of registers for register constraints in the
+   machine description.  Also define ranges of constants.
+
+   One of the classes must always be named ALL_REGS and include all hard regs.
+   If there is more than one class, another class must be named NO_REGS
+   and contain no registers.
+
+   The name GENERAL_REGS must be the name of a class (or an alias for
+   another name such as ALL_REGS).  This is the class of registers
+   that is allowed by "g" or "r" in a register constraint.
+   Also, registers outside this class are allocated only when
+   instructions express preferences for them.
+
+   The classes must be numbered in nondecreasing order; that is,
+   a larger-numbered class must never be contained completely
+   in a smaller-numbered class.
+
+   For any two classes, it is very desirable that there be another
+   class that represents their union.  */
+
+/* The M68hc11 has so fiew registers that it's not possible for GCC to
+   do any register allocation without breaking. We extend the processor
+   registers by having soft registers. These registers are treated as
+   hard registers by GCC but they are located in memory and accessed by page0
+   accesses (IND mode).  */
+enum reg_class
+{
+  NO_REGS,
+  D_REGS,                      /* 16-bit data register */
+  X_REGS,                      /* 16-bit X register */
+  Y_REGS,                      /* 16-bit Y register */
+  SP_REGS,                     /* 16 bit stack pointer */
+  DA_REGS,                     /* 8-bit A reg. */
+  DB_REGS,                     /* 8-bit B reg. */
+  Z_REGS,                      /* 16-bit fake Z register */
+  D8_REGS,                     /* 8-bit A or B reg. */
+  Q_REGS,                      /* 8-bit (byte (QI)) data (A, B or D) */
+  D_OR_X_REGS,                 /* D or X register */
+  D_OR_Y_REGS,                 /* D or Y register */
+  D_OR_SP_REGS,                        /* D or SP register */
+  X_OR_Y_REGS,                 /* IX or Y register */
+  A_REGS,                      /* 16-bit address register (X, Y, Z) */
+  X_OR_SP_REGS,                        /* X or SP register */
+  Y_OR_SP_REGS,                        /* Y or SP register */
+  X_OR_Y_OR_D_REGS,            /* X, Y or D */
+  A_OR_D_REGS,                 /* X, Y, Z or D */
+  A_OR_SP_REGS,                        /* X, Y, Z or SP */
+  H_REGS,                      /* 16-bit hard register (D, X, Y, Z, SP) */
+  S_REGS,                      /* 16-bit soft register */
+  D_OR_S_REGS,                 /* 16-bit soft register or D register */
+  X_OR_S_REGS,                 /* 16-bit soft register or X register */
+  Y_OR_S_REGS,                 /* 16-bit soft register or Y register */
+  SP_OR_S_REGS,                        /* 16-bit soft register or SP register */
+  D_OR_X_OR_S_REGS,            /* 16-bit soft register or D or X register */
+  D_OR_Y_OR_S_REGS,            /* 16-bit soft register or D or Y register */
+  D_OR_SP_OR_S_REGS,           /* 16-bit soft register or D or SP register */
+  A_OR_S_REGS,                 /* 16-bit soft register or X, Y registers */
+  D_OR_A_OR_S_REGS,            /* 16-bit soft register or D, X, Y registers */
+  TMP_REGS,                    /* 16 bit fake scratch register */
+  D_OR_A_OR_TMP_REGS,          /* General scratch register */
+  G_REGS,                      /* 16-bit general register
+                                   (H_REGS + soft registers) */
+  ALL_REGS,
+  LIM_REG_CLASSES
+};
+
+/* alias GENERAL_REGS to G_REGS. */
+#define GENERAL_REGS   G_REGS
+
+#define N_REG_CLASSES  (int) LIM_REG_CLASSES
+
+/* Give names of register classes as strings for dump file.   */
+#define REG_CLASS_NAMES \
+{ "NO_REGS",                                    \
+      "D_REGS",                                 \
+      "X_REGS",                                 \
+      "Y_REGS",                                 \
+      "SP_REGS",                                \
+      "DA_REGS",                                \
+      "DB_REGS",                                \
+      "D8_REGS",                                \
+      "Z_REGS",                                 \
+      "Q_REGS",                                 \
+      "D_OR_X_REGS",                            \
+      "D_OR_Y_REGS",                            \
+      "D_OR_SP_REGS",                           \
+      "X_OR_Y_REGS",                            \
+      "A_REGS",                                 \
+      "X_OR_SP_REGS",                           \
+      "Y_OR_SP_REGS",                           \
+      "X_OR_Y_OR_D_REGS",                       \
+      "A_OR_D_REGS",                            \
+      "A_OR_SP_REGS",                           \
+      "H_REGS",                                 \
+      "S_REGS",                                 \
+      "D_OR_S_REGS",                            \
+      "X_OR_S_REGS",                            \
+      "Y_OR_S_REGS",                            \
+      "SP_OR_S_REGS",                           \
+      "D_OR_X_OR_S_REGS",                       \
+      "D_OR_Y_OR_S_REGS",                       \
+      "D_OR_SP_OR_S_REGS",                      \
+      "A_OR_S_REGS",                            \
+      "D_OR_A_OR_S_REGS",                       \
+      "TMP_REGS",                              \
+      "D_OR_A_OR_TMP_REGS",                    \
+      "G_REGS",                                 \
+      "ALL_REGS" }
+
+/* An initializer containing the contents of the register classes,
+   as integers which are bit masks.  The Nth integer specifies the
+   contents of class N.  The way the integer MASK is interpreted is
+   that register R is in the class if `MASK & (1 << R)' is 1.  */
+
+/*--------------------------------------------------------------
+   X           0x00000001
+   D           0x00000002
+   Y           0x00000004
+   SP          0x00000008
+   PC          0x00000010
+   A           0x00000020
+   B           0x00000040
+   CCR         0x00000080
+   Z           0x00000100
+   FRAME        0x00000200
+   ZTMP                0x00000400
+   ZREG                0x00000800
+   XYREG       0x00001000
+   FAKE         0x00002000
+   Di          0xFFFFc000, 0x03FFF
+   SFRAME       0x00000000, 0x04000
+   AP           0x00000000, 0x08000
+
+   D_OR_X_REGS represents D+X. It is used for 32-bits numbers.
+   A_REGS      represents a valid base register for indexing. It represents
+              X,Y and the Z register.
+   S_REGS      represents the soft-registers. This includes the hard frame
+              and soft frame registers.
+--------------------------------------------------------------*/
+
+#define REG_CLASS_CONTENTS \
+/* NO_REGS */          {{ 0x00000000, 0x00000000 },                    \
+/* D_REGS  */           { 0x00000002, 0x00000000 }, /* D */            \
+/* X_REGS  */           { 0x00000001, 0x00000000 }, /* X */            \
+/* Y_REGS  */           { 0x00000004, 0x00000000 }, /* Y */            \
+/* SP_REGS */           { 0x00000008, 0x00000000 }, /* SP */           \
+/* DA_REGS */           { 0x00000020, 0x00000000 }, /* A */            \
+/* DB_REGS */           { 0x00000040, 0x00000000 }, /* B */            \
+/* D8_REGS */           { 0x00000060, 0x00000000 }, /* A B */          \
+/* Z_REGS  */           { 0x00000100, 0x00000000 }, /* Z */            \
+/* Q_REGS  */           { 0x00000062, 0x00000000 }, /* A B D */        \
+/* D_OR_X_REGS */        { 0x00000003, 0x00000000 }, /* D X */          \
+/* D_OR_Y_REGS */        { 0x00000006, 0x00000000 }, /* D Y */          \
+/* D_OR_SP_REGS */       { 0x0000000A, 0x00000000 }, /* D SP */         \
+/* X_OR_Y_REGS  */      { 0x00000005, 0x00000000 }, /* X Y */          \
+/* A_REGS  */           { 0x00000105, 0x00000000 }, /* X Y Z */        \
+/* X_OR_SP_REGS */       { 0x00000009, 0x00000000 }, /* X SP */         \
+/* Y_OR_SP_REGS */       { 0x0000000C, 0x00000000 }, /* Y SP */         \
+/* X_OR_Y_OR_D_REGS */   { 0x00000007, 0x00000000 }, /* D X Y */        \
+/* A_OR_D_REGS  */       { 0x00000107, 0x00000000 }, /* D X Y Z */      \
+/* A_OR_SP_REGS */       { 0x0000010D, 0x00000000 }, /* X Y SP */       \
+/* H_REGS  */           { 0x0000010F, 0x00000000 }, /* D X Y SP */     \
+/* S_REGS  */           { 0xFFFFDE00, 0x00007FFF }, /* _.D,..,FP,Z*  */  \
+/* D_OR_S_REGS */       { 0xFFFFDE02, 0x00007FFF }, /* D _.D */        \
+/* X_OR_S_REGS */       { 0xFFFFDE01, 0x00007FFF }, /* X _.D */        \
+/* Y_OR_S_REGS */       { 0xFFFFDE04, 0x00007FFF }, /* Y _.D */        \
+/* SP_OR_S_REGS */      { 0xFFFFDE08, 0x00007FFF }, /* SP _.D */       \
+/* D_OR_X_OR_S_REGS */  { 0xFFFFDE03, 0x00007FFF }, /* D X _.D */      \
+/* D_OR_Y_OR_S_REGS */  { 0xFFFFDE06, 0x00007FFF }, /* D Y _.D */      \
+/* D_OR_SP_OR_S_REGS */         { 0xFFFFDE0A, 0x00007FFF }, /* D SP _.D */     \
+/* A_OR_S_REGS */       { 0xFFFFDF05, 0x00007FFF }, /* X Y _.D */      \
+/* D_OR_A_OR_S_REGS */  { 0xFFFFDF07, 0x00007FFF }, /* D X Y _.D */    \
+/* TMP_REGS  */                 { 0x00002000, 0x00000000 }, /* FAKE */         \
+/* D_OR_A_OR_TMP_REGS*/  { 0x00002107, 0x00000000 }, /* D X Y Z Fake */  \
+/* G_REGS  */           { 0xFFFFFF1F, 0x00007FFF }, /* ? _.D D X Y */   \
+/* ALL_REGS*/           { 0xFFFFFFFF, 0x00007FFF }}
+
+
+/* set up a C expression whose value is a register class containing hard
+   register REGNO */
+#define Q_REGNO_P(REGNO)       ((REGNO) == HARD_A_REGNUM \
+                                || (REGNO) == HARD_B_REGNUM)
+#define Q_REG_P(X)              (REG_P (X) && Q_REGNO_P (REGNO (X)))
+
+#define D_REGNO_P(REGNO)        ((REGNO) == HARD_D_REGNUM)
+#define D_REG_P(X)              (REG_P (X) && D_REGNO_P (REGNO (X)))
+
+#define DB_REGNO_P(REGNO)       ((REGNO) == HARD_B_REGNUM)
+#define DB_REG_P(X)             (REG_P (X) && DB_REGNO_P (REGNO (X)))
+#define DA_REGNO_P(REGNO)       ((REGNO) == HARD_A_REGNUM)
+#define DA_REG_P(X)             (REG_P (X) && DA_REGNO_P (REGNO (X)))
+
+#define X_REGNO_P(REGNO)        ((REGNO) == HARD_X_REGNUM)
+#define X_REG_P(X)              (REG_P (X) && X_REGNO_P (REGNO (X)))
+
+#define Y_REGNO_P(REGNO)        ((REGNO) == HARD_Y_REGNUM)
+#define Y_REG_P(X)              (REG_P (X) && Y_REGNO_P (REGNO (X)))
+
+#define SP_REGNO_P(REGNO)       ((REGNO) == HARD_SP_REGNUM)
+#define SP_REG_P(X)             (REG_P (X) && SP_REGNO_P (REGNO (X)))
+
+/* Address register.  */
+#define A_REGNO_P(REGNO)        ((REGNO) == HARD_X_REGNUM \
+                                 || (REGNO) == HARD_Y_REGNUM \
+                                 || (REGNO) == HARD_Z_REGNUM)
+#define A_REG_P(X)              (REG_P (X) && A_REGNO_P (REGNO (X)))
+
+/* M68hc11 hard registers.  */
+#define H_REGNO_P(REGNO)        (D_REGNO_P (REGNO) || A_REGNO_P (REGNO) \
+                                || SP_REGNO_P (REGNO) || Q_REGNO_P (REGNO))
+#define H_REG_P(X)              (REG_P (X) && H_REGNO_P (REGNO (X)))
+
+#define FAKE_REGNO_P(REGNO)     ((REGNO) == FAKE_CLOBBER_REGNUM)
+#define FAKE_REG_P(X)           (REG_P (X) && FAKE_REGNO_P (REGNO (X)))
+
+/* Soft registers (or register emulation for gcc).  The temporary register
+   used by insn template must be part of the S_REGS class so that it
+   matches the 'u' constraint.  */
+#define S_REGNO_P(REGNO)        ((REGNO) >= SOFT_TMP_REGNUM \
+                                 && (REGNO) <= SOFT_REG_LAST \
+                                 && (REGNO) != FAKE_CLOBBER_REGNUM)
+#define S_REG_P(X)              (REG_P (X) && S_REGNO_P (REGNO (X)))
+
+#define Z_REGNO_P(REGNO)        ((REGNO) == HARD_Z_REGNUM)
+#define Z_REG_P(X)              (REG_P (X) && Z_REGNO_P (REGNO (X)))
+
+/* General register.  */
+#define G_REGNO_P(REGNO)        (H_REGNO_P (REGNO) || S_REGNO_P (REGNO) \
+                                 || ((REGNO) == HARD_PC_REGNUM) \
+                                || ((REGNO) == HARD_FP_REGNUM) \
+                                || ((REGNO) == SOFT_FP_REGNUM) \
+                                || ((REGNO) == FAKE_CLOBBER_REGNUM) \
+                                || ((REGNO) == SOFT_AP_REGNUM))
+
+#define G_REG_P(X)              (REG_P (X) && G_REGNO_P (REGNO (X)))
+
+#define REGNO_REG_CLASS(REGNO) \
+  (D_REGNO_P (REGNO) ? D_REGS : \
+   (X_REGNO_P (REGNO) ? X_REGS : \
+    (Y_REGNO_P (REGNO) ? Y_REGS : \
+     (SP_REGNO_P (REGNO) ? SP_REGS : \
+      (Z_REGNO_P (REGNO) ? Z_REGS : \
+       (H_REGNO_P (REGNO) ? H_REGS : \
+        (FAKE_REGNO_P (REGNO) ? TMP_REGS : \
+        (S_REGNO_P (REGNO) ? S_REGS : \
+         (DA_REGNO_P (REGNO) ? DA_REGS: \
+          (DB_REGNO_P (REGNO) ? DB_REGS: \
+            (G_REGNO_P (REGNO) ? G_REGS : ALL_REGS)))))))))))
+
+
+/* Get reg_class from a letter in the machine description.  */
+
+extern enum reg_class m68hc11_tmp_regs_class;
+#define REG_CLASS_FROM_LETTER(C) \
+   ((C) == 'a' ? DA_REGS : \
+    (C) == 'A' ? A_REGS : \
+    (C) == 'b' ? DB_REGS : \
+    (C) == 'B' ? X_OR_Y_REGS : \
+    (C) == 'd' ? D_REGS : \
+    (C) == 'D' ? D_OR_X_REGS : \
+    (C) == 'q' ? Q_REGS : \
+    (C) == 'h' ? H_REGS : \
+    (C) == 't' ? TMP_REGS : \
+    (C) == 'u' ? S_REGS : \
+    (C) == 'v' ? m68hc11_tmp_regs_class : \
+    (C) == 'w' ? SP_REGS : \
+    (C) == 'x' ? X_REGS : \
+    (C) == 'y' ? Y_REGS : \
+    (C) == 'z' ? Z_REGS : NO_REGS)
+
+#define PREFERRED_RELOAD_CLASS(X,CLASS)        preferred_reload_class(X,CLASS)
+
+
+#define LIMIT_RELOAD_CLASS(MODE, CLASS) limit_reload_class(MODE,CLASS)
+
+#define SMALL_REGISTER_CLASSES 1
+
+/* A C expression whose value is nonzero if pseudos that have been
+   assigned to registers of class CLASS would likely be spilled
+   because registers of CLASS are needed for spill registers.
+
+   The default value of this macro returns 1 if CLASS has exactly one
+   register and zero otherwise.  On most machines, this default
+   should be used.  Only define this macro to some other expression
+   if pseudo allocated by `local-alloc.c' end up in memory because
+   their hard registers were needed for spill registers.  If this
+   macro returns nonzero for those classes, those pseudos will only
+   be allocated by `global.c', which knows how to reallocate the
+   pseudo to another register.  If there would not be another
+   register available for reallocation, you should not change the
+   definition of this macro since the only effect of such a
+   definition would be to slow down register allocation.  */
+
+#define CLASS_LIKELY_SPILLED_P(CLASS)                                  \
+  (((CLASS) == D_REGS)                                                 \
+   || ((CLASS) == X_REGS)                                               \
+   || ((CLASS) == Y_REGS)                                               \
+   || ((CLASS) == A_REGS)                                               \
+   || ((CLASS) == SP_REGS)                                              \
+   || ((CLASS) == D_OR_X_REGS)                                          \
+   || ((CLASS) == D_OR_Y_REGS)                                          \
+   || ((CLASS) == X_OR_SP_REGS)                                         \
+   || ((CLASS) == Y_OR_SP_REGS)                                         \
+   || ((CLASS) == D_OR_SP_REGS))
+
+/* Return the maximum number of consecutive registers needed to represent
+   mode MODE in a register of class CLASS.  */
+#define CLASS_MAX_NREGS(CLASS, MODE)           \
+(((CLASS) == DA_REGS || (CLASS) == DB_REGS \
+   || (CLASS) == D8_REGS || (CLASS) == Q_REGS) ? GET_MODE_SIZE (MODE) \
+ : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))
+
+/* The letters I, J, K, L and M in a register constraint string
+   can be used to stand for particular ranges of immediate operands.
+   This macro defines what the ranges are.
+   C is the letter, and VALUE is a constant value.
+   Return 1 if VALUE is in the range specified by C.
+
+   `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)
+
+   'I', 'J', 'K' are not used.  */
+
+#define CONST_OK_FOR_LETTER_P(VALUE, C) \
+  ((C) == 'L' ? (VALUE) >= -65536 && (VALUE) <= 65535 : \
+   (C) == 'M' ? ((VALUE) & 0x0ffffL) == 0 : \
+   (C) == 'N' ? ((VALUE) == 1 || (VALUE) == -1): \
+   (C) == 'O' ? (VALUE) == 16 : \
+   (C) == 'P' ? (VALUE) <= 2 && (VALUE) >= -8 : 0)
+
+/* Similar, but for floating constants, and defining letters G and H.
+   No floating-point constants are valid on 68HC11.  */
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C)  0
+
+/* 'U' represents certain kind of memory indexed operand for 68HC12.
+   and any memory operand for 68HC11.  */
+#define EXTRA_CONSTRAINT(OP, C)                         \
+((C) == 'U' ? m68hc11_small_indexed_indirect_p (OP, GET_MODE (OP)) : 0)
+
+
+\f
+/* Stack layout; function entry, exit and calling.  */
+
+/* Define this if pushing a word on the stack
+   makes the stack pointer a smaller address.  */
+#define STACK_GROWS_DOWNWARD
+
+/* Define this if the nominal address of the stack frame
+   is at the high-address end of the local variables;
+   that is, each additional local variable allocated
+   goes at a more negative offset in the frame.
+
+   Don't define for 68HC11, the frame pointer is the bottom
+   of local variables.  */
+/* #define FRAME_GROWS_DOWNWARD */
+
+/* Define this if successive arguments to a function occupy decreasing 
+   addresses in the stack.  */
+/* #define ARGS_GROW_DOWNWARD */
+
+/* Offset within stack frame to start allocating local variables at.
+   If FRAME_GROWS_DOWNWARD, this is the offset to the END of the
+   first local allocated.  Otherwise, it is the offset to the BEGINNING
+   of the first local allocated.  */
+extern int m68hc11_sp_correction;
+#define STARTING_FRAME_OFFSET          m68hc11_sp_correction
+
+/* Offset of first parameter from the argument pointer register value.  */
+
+#define FIRST_PARM_OFFSET(FNDECL)      2
+
+/* A C expression whose value is RTL representing the location of the
+   incoming return address at the beginning of any function, before the
+   prologue.  This RTL is either a REG, indicating that the return
+   value is saved in REG, or a MEM representing a location in
+   the stack.
+  
+   Before the prologue, RA is at 0(sp). */
+#define INCOMING_RETURN_ADDR_RTX \
+    gen_rtx_MEM (VOIDmode, gen_rtx_REG (VOIDmode, STACK_POINTER_REGNUM))
+
+/* Before the prologue, the top of the frame is at 2(sp).  */
+#define INCOMING_FRAME_SP_OFFSET        2
+
+/* Define this if functions should assume that stack space has been
+   allocated for arguments even when their values are passed in
+   registers.
+  
+   The value of this macro is the size, in bytes, of the area reserved for
+   arguments passed in registers.
+  
+   This space can either be allocated by the caller or be a part of the
+   machine-dependent stack frame: `OUTGOING_REG_PARM_STACK_SPACE'
+   says which. */
+/* #define REG_PARM_STACK_SPACE(FNDECL)        2 */
+
+/* Define this macro if REG_PARM_STACK_SPACE is defined but stack
+   parameters don't skip the area specified by REG_PARM_STACK_SPACE.
+   Normally, when a parameter is not passed in registers, it is placed on
+   the stack beyond the REG_PARM_STACK_SPACE area.  Defining this macro  
+   suppresses this behavior and causes the parameter to be passed on the
+   stack in its natural location.  */
+/* #define STACK_PARMS_IN_REG_PARM_AREA */
+
+/* Register to use for pushing function arguments.  */
+#define STACK_POINTER_REGNUM           HARD_SP_REGNUM
+
+/* Base register for access to local variables of the function.  */
+#define FRAME_POINTER_REGNUM           SOFT_FP_REGNUM
+
+#define HARD_FRAME_POINTER_REGNUM      HARD_FP_REGNUM
+
+/* Base register for access to arguments of the function.  */
+#define ARG_POINTER_REGNUM             SOFT_AP_REGNUM
+
+/* Register in which static-chain is passed to a function.  */
+#define STATIC_CHAIN_REGNUM            SOFT_REG_FIRST
+
+\f
+/* Definitions for register eliminations.
+
+   This is an array of structures.  Each structure initializes one pair
+   of eliminable registers.  The "from" register number is given first,
+   followed by "to".  Eliminations of the same "from" register are listed
+   in order of preference.
+
+   We have two registers that are eliminated on the 6811. The psuedo arg
+   pointer and pseudo frame pointer registers can always be eliminated;
+   they are replaced with either the stack or the real frame pointer. */
+
+#define ELIMINABLE_REGS                                        \
+{{ARG_POINTER_REGNUM,   STACK_POINTER_REGNUM},         \
+ {ARG_POINTER_REGNUM,   HARD_FRAME_POINTER_REGNUM},    \
+ {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM},         \
+ {FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
+
+/* Value should be nonzero if functions must have frame pointers.
+   Zero means the frame pointer need not be set up (and parms may be
+   accessed via the stack pointer) in functions that seem suitable.
+   This is computed in `reload', in reload1.c. */
+#define FRAME_POINTER_REQUIRED 0
+
+/* Given FROM and TO register numbers, say whether this elimination is allowed.
+   Frame pointer elimination is automatically handled.
+
+   All other eliminations are valid.  */
+
+#define CAN_ELIMINATE(FROM, TO)                                        \
+ ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \
+  ? ! frame_pointer_needed                                     \
+  : 1)
+
+
+/* Define the offset between two registers, one to be eliminated, and the other
+   its replacement, at the start of a routine.  */
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)                   \
+    { OFFSET = m68hc11_initial_elimination_offset (FROM, TO); }
+
+/* LONGJMP_RESTORE_FROM_STACK */
+
+\f
+/* Passing Function Arguments on the Stack.  */
+
+/* When a prototype says `char' or `short', really pass an `int'.  */
+/* #define PROMOTE_PROTOTYPES */
+
+/* If we generate an insn to push BYTES bytes, this says how many the
+   stack pointer really advances by. No rounding or alignment needed
+   for MC6811. */
+#define PUSH_ROUNDING(BYTES)   (BYTES)
+
+/* Value is 1 if returning from a function call automatically pops the
+   arguments described by the number-of-args field in the call. FUNTYPE is
+   the data type of the function (as a tree), or for a library call it is
+   an identifier node for the subroutine name.
+  
+   The standard MC6811 call, with arg count word, includes popping the
+   args as part of the call template. */
+#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
+
+/* Nonzero if type TYPE should be returned in memory.
+   Blocks and data types largers than 4 bytes cannot be returned
+   in the register (D + X = 4).  */
+#define RETURN_IN_MEMORY(TYPE)                         \
+    ((TYPE_MODE (TYPE) == BLKmode)                     \
+     ? (int_size_in_bytes (TYPE) > 4)                  \
+     : (GET_MODE_SIZE (TYPE_MODE (TYPE)) > 4))
+
+\f
+/* Passing Arguments in Registers.  */
+
+/* Define a data type for recording info about an argument list
+   during the scan of that argument list.  This data type should
+   hold all necessary information about the function itself
+   and about the args processed so far, enough to enable macros
+   such as FUNCTION_ARG to determine where the next arg should go.  */
+
+typedef struct m68hc11_args
+{
+  int words;
+  int nregs;
+} CUMULATIVE_ARGS;
+
+/* A C expression that indicates when an argument must be passed by reference.
+   If nonzero for an argument, a copy of that argument is made in memory and a
+   pointer to the argument is passed instead of the argument itself.
+   The pointer is passed in whatever way is appropriate for passing a pointer
+   to that type.
+   64-bit numbers are passed by reference. */
+#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \
+    m68hc11_function_arg_pass_by_reference (& (CUM), (MODE), (TYPE), (NAMED))
+
+
+/* If defined, a C expression which determines whether, and in which direction,
+   to pad out an argument with extra space.  The value should be of type
+   `enum direction': either `upward' to pad above the argument,
+   `downward' to pad below, or `none' to inhibit padding.
+
+   Structures are stored left shifted in their argument slot.  */
+#define FUNCTION_ARG_PADDING(MODE, TYPE) \
+  m68hc11_function_arg_padding ((MODE), (TYPE))
+
+/* A C expression that indicates when it is the called function's
+   responsibility to make a copy of arguments passed by invisible
+   reference.  Normally, the caller makes a copy and passes the
+   address of the copy to the routine being called.  When
+   FUNCTION_ARG_CALLEE_COPIES is defined and is nonzero, the caller
+   does not make a copy.  Instead, it passes a pointer to the "live"
+   value.  The called function must not modify this value.  If it can
+   be determined that the value won't be modified, it need not make a
+   copy; otherwise a copy must be made. */
+#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED)             \
+    ((NAMED) && FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED))
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS for a call to a
+   function whose data type is FNTYPE. For a library call, FNTYPE is 0. */
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT) \
+    (m68hc11_init_cumulative_args (&CUM, FNTYPE, LIBNAME))
+
+/* Update the data in CUM to advance over an argument of mode MODE and data
+   type TYPE. (TYPE is null for libcalls where that information may not be
+   available.) */
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
+    (m68hc11_function_arg_advance (&CUM, MODE, TYPE, NAMED))
+
+/* Define where to put the arguments to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
+   NAMED is nonzero if this argument is a named parameter
+    (otherwise it is an extra parameter matching an ellipsis).  */
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+  (m68hc11_function_arg (&CUM, MODE, TYPE, NAMED))
+
+/* Define the profitability of saving registers around calls.
+
+   Disable this because the saving instructions generated by
+   caller-save need a reload and the way it is implemented,
+   it forbids all spill registers at that point.  Enabling
+   caller saving results in spill failure.  */
+#define CALLER_SAVE_PROFITABLE(REFS,CALLS) 0
+
+/* Implement `va_arg'.  */
+#define EXPAND_BUILTIN_VA_START(stdarg, valist, nextarg) \
+  m68hc11_expand_builtin_va_start (stdarg, valist, nextarg)
+
+#define EXPAND_BUILTIN_VA_ARG(valist, type) \
+  m68hc11_va_arg (valist, type)
+
+#define FUNCTION_EPILOGUE(FILE, SIZE)  m68hc11_function_epilogue(FILE, SIZE)
+
+/* For an arg passed partly in registers and partly in memory,
+   this is the number of registers used.
+   For args passed entirely in registers or entirely in memory, zero.
+
+   Passing an arg partly in register and memory does not work at all.
+   Don't do that.  */
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) (0)
+
+/* 1 if N is a possible register number for function argument passing.
+   D is for 16-bit values, X is for 32-bit (X+D).  */
+#define FUNCTION_ARG_REGNO_P(N)        \
+     (((N) == HARD_D_REGNUM) || ((N) == HARD_X_REGNUM))
+
+/* All return values are in the D or X+D registers:
+    - 8 and 16-bit values are returned in D.
+      BLKmode are passed in D as pointer.
+    - 32-bit values are returned in X + D.
+      The high part is passed in X and the low part in D.
+      For GCC, the register number must be HARD_X_REGNUM.  */
+#define FUNCTION_VALUE(VALTYPE, FUNC)                                  \
+     gen_rtx (REG, TYPE_MODE (VALTYPE),                                        \
+              ((TYPE_MODE (VALTYPE) == BLKmode                         \
+               || GET_MODE_SIZE (TYPE_MODE (VALTYPE)) <= 2)            \
+                  ? HARD_D_REGNUM : HARD_X_REGNUM))
+
+#define LIBCALL_VALUE(MODE)                                            \
+     gen_rtx (REG, MODE,                                               \
+              (((MODE) == BLKmode || GET_MODE_SIZE (MODE) <= 2)                \
+                   ? HARD_D_REGNUM : HARD_X_REGNUM))
+
+/* 1 if N is a possible register number for a function value.  */
+#define FUNCTION_VALUE_REGNO_P(N) \
+     ((N) == HARD_D_REGNUM || (N) == HARD_X_REGNUM)
+
+/* Register in which address to store a structure value is passed to a
+   function.  */
+#define STRUCT_VALUE_REGNUM    HARD_D_REGNUM
+
+/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function,
+   the stack pointer does not matter.  The value is tested only in functions
+   that have frame pointers. No definition is equivalent to always zero.  */
+#define EXIT_IGNORE_STACK      0
+
+\f
+/* Generating Code for Profiling.  */
+
+/* Output assembler code to FILE to increment profiler label # LABELNO
+   for profiling a function entry.  */
+#define FUNCTION_PROFILER(FILE, LABELNO)               \
+    asm_fprintf (FILE, "\tldy LP%d\n\tjsr mcount\n", (LABELNO))
+
+/* Output assembler code to FILE to initialize this source file's
+   basic block profiling info, if that has not already been done. */
+#define FUNCTION_BLOCK_PROFILER(FILE, BLOCK_OR_LABEL)  \
+    m68hc11_function_block_profiler(FILE, BLOCK_OR_LABEL)
+
+/* Output assembler code to FILE to increment the counter for
+  the BLOCKNO'th basic block in this source file.  */
+#define BLOCK_PROFILER(FILE, BLOCKNO)                  \
+    m68hc11_block_profiler(FILE, BLOCKNO)
+
+/* Output assembler code to FILE to indicate return from 
+   a function during basic block profiling.  */
+#define FUNCTION_BLOCK_PROFILER_EXIT(FILE)             \
+    asm_fprintf (FILE, "\tjsr %U__bb_trace_ret\n");
+
+/* Save all registers which may be clobbered by a function call.
+   MACHINE_STATE_SAVE and MACHINE_STATE_RESTORE are target-code macros,
+   used in libgcc2.c.  They may not refer to TARGET_* macros !!!
+
+   We don't need to save the CCR nor the soft registers because
+   they will be saved by gcc.  */
+#define MACHINE_STATE_SAVE(id) \
+  {                           \
+    asm ("pshy");             \
+    asm ("pshx");             \
+    asm ("psha");             \
+    asm ("pshb");             \
+  }
+
+#define MACHINE_STATE_RESTORE(id) \
+  {                           \
+    asm ("pulb");             \
+    asm ("pula");             \
+    asm ("pulx");             \
+    asm ("puly");             \
+  }
+
+/* Output assembler code for a block containing the constant parts
+   of a trampoline, leaving space for the variable parts.  */
+#define TRAMPOLINE_TEMPLATE(FILE) { \
+  fprintf (FILE, "\t.bogus\t\t; TRAMPOLINE_TEMPLATE unimplemented\n"); }
+
+/* Length in units of the trampoline for entering a nested function.  */
+#define TRAMPOLINE_SIZE                0
+
+/* A C statement to initialize the variable parts of a trampoline.
+   ADDR is an RTX for the address of the trampoline; FNADDR is an
+   RTX for the address of the nested function; STATIC_CHAIN is an
+   RTX for the static chain value that should be passed to the
+   function when it is called.  */
+#define INITIALIZE_TRAMPOLINE(TRAMP, FNADDR, CXT) { \
+       }
+\f
+
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+   with arguments ARGS is a valid machine specific attribute for DECL.
+   The attributes in ATTRIBUTES have previously been assigned to DECL.  */
+
+#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTRIBUTES, NAME, ARGS) \
+  (m68hc11_valid_decl_attribute_p (DECL, ATTRIBUTES, NAME, ARGS))
+
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+   with arguments ARGS is a valid machine specific attribute for TYPE.
+   The attributes in ATTRIBUTES have previously been assigned to TYPE.  */
+
+#define VALID_MACHINE_TYPE_ATTRIBUTE(TYPE, ATTRIBUTES, NAME, ARGS) \
+  (m68hc11_valid_type_attribute_p (TYPE, ATTRIBUTES, NAME, ARGS))
+
+/* If defined, a C expression whose value is zero if the attributes on
+   TYPE1 and TYPE2 are incompatible, one if they are compatible, and
+   two if they are nearly compatible (which causes a warning to be
+   generated).  */
+
+#define COMP_TYPE_ATTRIBUTES(TYPE1, TYPE2) \
+  (m68hc11_comp_type_attributes (TYPE1, TYPE2))
+
+/* If defined, a C statement that assigns default attributes to newly
+   defined TYPE.  */
+
+#define SET_DEFAULT_TYPE_ATTRIBUTES(TYPE) \
+  (m68hc11_set_default_type_attributes (TYPE))
+
+/* Define this macro if references to a symbol must be treated
+   differently depending on something about the variable or function
+   named by the symbol (such as what section it is in).
+
+   For the 68HC11, we want to recognize trap handlers so that we
+   handle calls to traps in a special manner (by issuing the trap).
+   This information is stored in SYMBOL_REF_FLAG.  */
+
+#define ENCODE_SECTION_INFO(DECL) m68hc11_encode_section_info (DECL)
+
+/* Override what GCC does for section info to let us recognize traps.  */
+
+#define REDO_SECTION_INFO_P(DECL) 1
+
+/* `INIT_TARGET_OPTABS'
+     Define this macro as a C statement that declares additional library
+     routines renames existing ones. `init_optabs' calls this macro
+     after initializing all the normal library routines.
+
+     Overrides the memcpy */
+
+#define INIT_TARGET_OPTABS                                             \
+do                                                                     \
+  {                                                                    \
+    memcpy_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__memcpy");           \
+    memcmp_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__memcmp");           \
+    memset_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__memset");           \
+  }                                                                    \
+while (0)
+
+\f
+/* Addressing modes, and classification of registers for them.  */
+
+/* The 68HC12 has all the post/pre increment/decrement modes.  */
+#define HAVE_POST_INCREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+#define HAVE_PRE_INCREMENT  (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+#define HAVE_POST_DECREMENT (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+#define HAVE_PRE_DECREMENT  (TARGET_M6812 && TARGET_AUTO_INC_DEC)
+
+/* The class value for base registers.  This depends on the target:
+   A_REGS for 68HC11 and A_OR_SP_REGS for 68HC12.  The class value
+   is stored at init time.  */
+extern enum reg_class m68hc11_base_reg_class;
+#define BASE_REG_CLASS         m68hc11_base_reg_class
+
+/* The class value for index registers.  This is NO_REGS for 68HC11.  */
+
+extern enum reg_class m68hc11_index_reg_class;
+#define INDEX_REG_CLASS                m68hc11_index_reg_class
+
+/* These assume that REGNO is a hard or pseudo reg number. They give nonzero
+   only if REGNO is a hard reg of the suitable class or a pseudo reg currently
+   allocated to a suitable hard reg.  Since they use reg_renumber, they are
+   safe only once reg_renumber has been allocated, which happens in
+   local-alloc.c.  */
+
+
+/* Internal macro, return 1 if REGNO is a valid base register.  */
+#if GCC_VERSION == 2095
+# define REG_VALID_P(REGNO) ((REGNO) >= 0)
+#else
+# define REG_VALID_P(REGNO) (1)        /* ? */
+#endif
+
+extern unsigned char m68hc11_reg_valid_for_base[FIRST_PSEUDO_REGISTER];
+#define REG_VALID_FOR_BASE_P(REGNO) \
+    (REG_VALID_P (REGNO) && (REGNO) < FIRST_PSEUDO_REGISTER \
+     && m68hc11_reg_valid_for_base[REGNO])
+
+/* Internal macro, return 1 if REGNO is a valid index register.  */
+extern unsigned char m68hc11_reg_valid_for_index[FIRST_PSEUDO_REGISTER];
+#define REG_VALID_FOR_INDEX_P(REGNO) \
+    (REG_VALID_P (REGNO) >= 0 && (REGNO) < FIRST_PSEUDO_REGISTER \
+     && m68hc11_reg_valid_for_index[REGNO])
+
+/* Internal macro, the nonstrict definition for REGNO_OK_FOR_BASE_P.  */
+#define REGNO_OK_FOR_BASE_NONSTRICT_P(REGNO) \
+    ((REGNO) >= FIRST_PSEUDO_REGISTER \
+     || REG_VALID_FOR_BASE_P (REGNO) \
+     || (REGNO) == FRAME_POINTER_REGNUM \
+     || (REGNO) == HARD_FRAME_POINTER_REGNUM \
+     || (REGNO) == ARG_POINTER_REGNUM \
+     || (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO])))
+
+/* Internal macro, the nonstrict definition for REGNO_OK_FOR_INDEX_P.  */
+#define REGNO_OK_FOR_INDEX_NONSTRICT_P(REGNO) \
+    (TARGET_M6812 \
+     && ((REGNO) >= FIRST_PSEUDO_REGISTER \
+         || REG_VALID_FOR_INDEX_P (REGNO) \
+         || (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))))
+
+/* Internal macro, the strict definition for REGNO_OK_FOR_BASE_P.  */
+#define REGNO_OK_FOR_BASE_STRICT_P(REGNO) \
+    ((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_BASE_P (REGNO) \
+     : (reg_renumber && REG_VALID_FOR_BASE_P (reg_renumber[REGNO])))
+
+/* Internal macro, the strict definition for REGNO_OK_FOR_INDEX_P.  */
+#define REGNO_OK_FOR_INDEX_STRICT_P(REGNO) \
+    (TARGET_M6812 \
+     && ((REGNO) < FIRST_PSEUDO_REGISTER ? REG_VALID_FOR_INDEX_P (REGNO) \
+         : (reg_renumber && REG_VALID_FOR_INDEX_P (reg_renumber[REGNO]))))
+
+#define REGNO_OK_FOR_BASE_P2(REGNO,STRICT) \
+    ((STRICT) ? (REGNO_OK_FOR_BASE_STRICT_P (REGNO)) \
+              : (REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO)))
+
+#define REGNO_OK_FOR_INDEX_P2(REGNO,STRICT) \
+    ((STRICT) ? (REGNO_OK_FOR_INDEX_STRICT_P (REGNO)) \
+              : (REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO)))
+
+#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_BASE_STRICT_P (REGNO)
+#define REGNO_OK_FOR_INDEX_P(REGNO) REGNO_OK_FOR_INDEX_STRICT_P (REGNO)
+
+#define REG_OK_FOR_BASE_STRICT_P(X)     REGNO_OK_FOR_BASE_STRICT_P (REGNO (X))
+#define REG_OK_FOR_BASE_NONSTRICT_P(X)  REGNO_OK_FOR_BASE_NONSTRICT_P (REGNO (X))
+#define REG_OK_FOR_INDEX_STRICT_P(X)    REGNO_OK_FOR_INDEX_STRICT_P (REGNO (X))
+#define REG_OK_FOR_INDEX_NONSTRICT_P(X) REGNO_OK_FOR_INDEX_NONSTRICT_P (REGNO (X))
+
+/* see PUSH_POP_ADDRESS_P() below for an explanation of this.  */
+#define IS_STACK_PUSH(operand) \
+    ((GET_CODE (operand) == MEM) \
+     && (GET_CODE (XEXP (operand, 0)) == PRE_DEC) \
+     && (SP_REG_P (XEXP (XEXP (operand, 0), 0))))
+
+#define IS_STACK_POP(operand) \
+    ((GET_CODE (operand) == MEM) \
+     && (GET_CODE (XEXP (operand, 0)) == POST_INC) \
+     && (SP_REG_P (XEXP (XEXP (operand, 0), 0))))
+
+/* 1 if X is an rtx for a constant that is a valid address.  */
+#define CONSTANT_ADDRESS_P(X)  (CONSTANT_P (X))
+
+/* Maximum number of registers that can appear in a valid memory address */
+#define MAX_REGS_PER_ADDRESS   2
+
+/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression that is a
+   valid memory address for an instruction. The MODE argument is the
+   machine mode for the MEM expression that wants to use this address.  */
+
+/*--------------------------------------------------------------
+   Valid addresses are either direct or indirect (MEM) versions
+   of the following forms:
+       constant                N
+       register                ,X
+       indexed                 N,X
+--------------------------------------------------------------*/
+
+/* The range of index that is allowed by indirect addressing. */
+
+#define VALID_MIN_OFFSET m68hc11_min_offset
+#define VALID_MAX_OFFSET m68hc11_max_offset
+
+/* The offset values which are allowed by the n,x and n,y addressing modes.
+   Take into account the size of the mode because we may have to add
+   a mode offset to access the lowest part of the data.
+   (For example, for an SImode, the last valid offset is 252.) */
+#define VALID_CONSTANT_OFFSET_P(X,MODE)                \
+((GET_CODE (X) == CONST_INT) &&                        \
+ ((INTVAL (X) >= VALID_MIN_OFFSET)             \
+    && ((INTVAL (X) <= VALID_MAX_OFFSET                \
+               - (HOST_WIDE_INT) (GET_MODE_SIZE (MODE) + 1)))))
+
+/* This is included to allow stack push/pop operations. Special hacks in the
+   md and m6811.c files exist to support this.  */
+#define PUSH_POP_ADDRESS_P(X) \
+  (((GET_CODE (X) == PRE_DEC) || (GET_CODE (X) == POST_INC)) \
+       && SP_REG_P (XEXP (X, 0)))
+
+/* Go to ADDR if X is a valid address. */
+#ifndef REG_OK_STRICT
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR) \
+{ \
+  if (m68hc11_go_if_legitimate_address ((X), (MODE), 0)) goto ADDR; \
+}
+#else
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR)                 \
+{                                                       \
+  if (m68hc11_go_if_legitimate_address ((X), (MODE), 1)) goto ADDR; \
+}
+#endif
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx and check its
+   validity for a certain class.  We have two alternate definitions for each
+   of them.  The usual definition accepts all pseudo regs; the other rejects
+   them unless they have been allocated suitable hard regs.  The symbol
+   REG_OK_STRICT causes the latter definition to be used.
+  
+   Most source files want to accept pseudo regs in the hope that they will
+   get allocated to the class that the insn wants them to be in. Source files
+   for reload pass need to be strict. After reload, it makes no difference,
+   since pseudo regs have been eliminated by then.  */
+
+#ifndef REG_OK_STRICT
+/* Nonzero if X is a hard reg that can be used as a base reg.  */
+#define REG_OK_FOR_BASE_P(X)   REG_OK_FOR_BASE_NONSTRICT_P(X)
+
+/* Nonzero if X is a hard reg that can be used as an index.  */
+#define REG_OK_FOR_INDEX_P(X)  REG_OK_FOR_INDEX_NONSTRICT_P(X)
+#else
+#define REG_OK_FOR_BASE_P(X)   REG_OK_FOR_BASE_STRICT_P(X)
+#define REG_OK_FOR_INDEX_P(X)  REG_OK_FOR_INDEX_STRICT_P(X)
+#endif
+
+
+/* Try machine-dependent ways of modifying an illegitimate address
+   to be legitimate.  If we find one, return the new, valid address.
+   This macro is used in only one place: `memory_address' in explow.c.
+  
+   OLDX is the address as it was before break_out_memory_refs was called.
+   In some cases it is useful to look at this to decide what needs to be done.
+  
+   MODE and WIN are passed so that this macro can use
+   GO_IF_LEGITIMATE_ADDRESS.
+  
+   It is always safe for this macro to do nothing.
+   It exists to recognize opportunities to optimize the output.  */
+
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN)                     \
+{ rtx operand = (X);                                            \
+  if (m68hc11_legitimize_address (&operand, (OLDX), (MODE)))   \
+    {                                                           \
+      (X) = operand;                                            \
+      GO_IF_LEGITIMATE_ADDRESS (MODE,X,WIN);                    \
+    }                                                           \
+}
+
+/* Go to LABEL if ADDR (a legitimate address expression)
+   has an effect that depends on the machine mode it is used for.  */
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL)  \
+{                                                                      \
+  if (GET_CODE (ADDR) == PRE_DEC || GET_CODE (ADDR) == POST_DEC                \
+      || GET_CODE (ADDR) == PRE_INC || GET_CODE (ADDR) == POST_INC)    \
+    goto LABEL;                                                                \
+}
+
+/* Nonzero if the constant value X is a legitimate general operand.
+   It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE.  */
+
+#define LEGITIMATE_CONSTANT_P(X)       1
+
+\f
+/* Tell final.c how to eliminate redundant test instructions.  */
+
+#define NOTICE_UPDATE_CC(EXP, INSN) \
+       m68hc11_notice_update_cc ((EXP), (INSN))
+
+/* Compute the cost of computing a constant rtl expression RTX whose rtx-code
+   is CODE.  The body of this macro is a portion of a switch statement.  If
+   the code is computed here, return it with a return statement. Otherwise,
+   break from the switch.  */
+#define CONST_COSTS(RTX,CODE,OUTER_CODE) \
+ case CONST_INT:                        \
+    if (RTX == const0_rtx) return 0;    \
+ case CONST:                            \
+    return 0;                            \
+ case LABEL_REF:                        \
+ case SYMBOL_REF:                       \
+   return 1;                            \
+ case CONST_DOUBLE:                     \
+   return 0;
+
+#define DEFAULT_RTX_COSTS(X,CODE,OUTER_CODE)           \
+    return m68hc11_rtx_costs (X, CODE, OUTER_CODE);
+
+
+/* An expression giving the cost of an addressing mode that contains
+   ADDRESS.  If not defined, the cost is computed from the ADDRESS
+   expression and the `CONST_COSTS' values.  */
+
+#define ADDRESS_COST(RTX) m68hc11_address_cost (RTX)
+
+/* Move costs between classes of registers */
+#define REGISTER_MOVE_COST(CLASS1, CLASS2)     \
+    (m68hc11_register_move_cost (CLASS1, CLASS2))
+
+/* Move cost between register and memory.
+    - Move to a 16-bit register is reasonable,
+    - Move to a soft register can be expensive.  */
+#define MEMORY_MOVE_COST(MODE,CLASS,IN)                \
+    m68hc11_memory_move_cost ((MODE),(CLASS),(IN))
+
+/* A C expression for the cost of a branch instruction.  A value of 1
+   is the default; other values are interpreted relative to that.
+
+   Pretend branches are cheap because GCC generates sub-optimal code
+   for the default value.  */
+#define BRANCH_COST 0
+
+/* Nonzero if access to memory by bytes is slow and undesirable.  */
+#define SLOW_BYTE_ACCESS       0
+
+/* It is as good to call a constant function address as to call an address
+   kept in a register.  */
+#define NO_FUNCTION_CSE
+
+/* Try a machine-dependent way of reloading an illegitimate address
+   operand.  If we find one, push the reload and jump to WIN.  This
+   macro is used in only one place: `find_reloads_address' in reload.c.
+
+   For M68HC11, we handle large displacements of a base register
+   by splitting the addend accors an addhi3 insn.
+
+   For M68HC12, the 64K offset range is available.
+   */
+
+#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_LEVELS,WIN)     \
+do {                                                                    \
+  /* We must recognize output that we have already generated ourselves.  */ \
+  if (GET_CODE (X) == PLUS                                             \
+      && GET_CODE (XEXP (X, 0)) == PLUS                                        \
+      && GET_CODE (XEXP (XEXP (X, 0), 0)) == REG                       \
+      && GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT                 \
+      && GET_CODE (XEXP (X, 1)) == CONST_INT)                          \
+    {                                                                  \
+      push_reload (XEXP (X, 0), NULL_RTX, &XEXP (X, 0), NULL_PTR,       \
+                   BASE_REG_CLASS, GET_MODE (X), VOIDmode, 0, 0,        \
+                   OPNUM, TYPE);                                        \
+      goto WIN;                                                         \
+    }                                                                  \
+  if (GET_CODE (X) == PLUS                                              \
+      && GET_CODE (XEXP (X, 0)) == REG                                  \
+      && GET_CODE (XEXP (X, 1)) == CONST_INT                           \
+      && !VALID_CONSTANT_OFFSET_P (XEXP (X, 1), MODE))                  \
+    {                                                                   \
+      HOST_WIDE_INT val = INTVAL (XEXP (X, 1));                         \
+      HOST_WIDE_INT low, high;                                          \
+      high = val & (~0x0FF);                                            \
+      low  = val & 0x00FF;                                              \
+      if (low >= 256-15) { high += 16; low -= 16; }                     \
+      /* Reload the high part into a base reg; leave the low part       \
+         in the mem directly.  */                                       \
+                                                                        \
+      X = gen_rtx_PLUS (Pmode,                                         \
+                        gen_rtx_PLUS (Pmode, XEXP (X, 0),              \
+                                      GEN_INT (high)),                  \
+                        GEN_INT (low));                                 \
+                                                                        \
+      push_reload (XEXP (X, 0), NULL_RTX, &XEXP (X, 0), NULL_PTR,       \
+                   BASE_REG_CLASS, GET_MODE (X), VOIDmode, 0, 0,        \
+                   OPNUM, TYPE);                                        \
+      goto WIN;                                                         \
+    }                                                                   \
+} while (0)
+
+\f
+/* Defining the Output Assembler Language.  */
+
+/* A default list of other sections which we might be "in" at any given
+   time.  For targets that use additional sections (e.g. .tdesc) you
+   should override this definition in the target-specific file which
+   includes this file.  */
+
+/* Output before read-only data.  */
+#define TEXT_SECTION_ASM_OP    ("\t.sect\t.text")
+
+/* Output before writable data.  */
+#define DATA_SECTION_ASM_OP    ("\t.sect\t.data")
+
+/* Output before uninitialized data.  */
+#define BSS_SECTION_ASM_OP     ("\t.sect\t.bss")
+
+/* This is how to begin an assembly language file.  Most svr4 assemblers want
+   at least a .file directive to come first, and some want to see a .version
+   directive come right after that.  Here we just establish a default
+   which generates only the .file directive.  If you need a .version
+   directive for any specific target, you should override this definition
+   in the target-specific file which includes this one.  */
+
+#undef ASM_FILE_START
+#define ASM_FILE_START(FILE)                            \
+    m68hc11_asm_file_start ((FILE), main_input_filename)
+
+/* Comment character */
+#define ASM_COMMENT_START      ";"
+
+/* Output to assembler file text saying following lines
+   may contain character constants, extra white space, comments, etc.  */
+#define ASM_APP_ON             "; Begin inline assembler code\n#APP\n"
+
+/* Output to assembler file text saying following lines
+   no longer contain unusual constructs.  */
+#define ASM_APP_OFF            "; End of inline assembler code\n#NO_APP\n"
+
+/* Output #ident as a .ident.  */
+
+/* This is how to output a `long double' extended real constant.  */
+
+#define ASM_OUTPUT_LONG_DOUBLE(FILE,VALUE)                             \
+    ASM_OUTPUT_DOUBLE(FILE,VALUE)
+
+/* This is how to output an assembler line defining a `double' constant.  */
+
+#define ASM_OUTPUT_DOUBLE(FILE,VALUE)                                  \
+do { long l[2];                                                                \
+     REAL_VALUE_TO_TARGET_DOUBLE (VALUE, l);                           \
+     fprintf (FILE, "\t%s\t0x%lx,0x%lx\n", ASM_LONG, l[0], l[1]);      \
+   } while (0)
+
+/* This is how to output an assembler line defining a `float' constant.  */
+
+#define ASM_OUTPUT_FLOAT(FILE,VALUE)                   \
+do { long l;                                           \
+     REAL_VALUE_TO_TARGET_SINGLE (VALUE, l);           \
+     fprintf ((FILE), "\t%s\t0x%lx\n", ASM_LONG, l);   \
+   } while (0)
+
+/* This is how to output an assembler line defining a `long' constant.  */
+#define ASM_OUTPUT_INT(FILE,VALUE)                     \
+( fprintf (FILE, "\t%s\t", ASM_LONG),                  \
+  output_addr_const (FILE, (VALUE)),                   \
+  fprintf (FILE, "\n"))
+
+/* Likewise for `char' and `short' constants.  */
+#define ASM_OUTPUT_SHORT(FILE,VALUE)                   \
+( fprintf (FILE, "\t%s\t", ASM_SHORT),                 \
+  output_addr_const (FILE, (VALUE)),                   \
+  fprintf (FILE, "\n"))
+
+/* This is how to output an assembler line for a numeric constant byte.  */
+#define ASM_OUTPUT_CHAR(FILE,VALUE)                    \
+( fprintf (FILE, "\t%s\t", ASM_BYTE_OP),               \
+  output_addr_const (FILE, (VALUE)),                   \
+  putc ('\n', FILE))
+
+#define ASM_OUTPUT_BYTE(FILE,VALUE)                    \
+  fprintf ((FILE), "%s 0x%x\n", ASM_BYTE_OP, (VALUE))
+
+
+/* Define the parentheses used to group arithmetic operations in assembler
+ * code.  
+ */
+#define ASM_OPEN_PAREN         "("
+#define ASM_CLOSE_PAREN                ")"
+
+/* This is how to output the definition of a user-level label named NAME,
+   such as the label on a static function or variable NAME.  */
+
+#define ASM_OUTPUT_LABEL(FILE,NAME)    \
+  do { assemble_name (FILE, NAME); fputs (":\n", FILE); } while (0)
+
+
+/* This is how to output a command to make the user-level label named NAME
+   defined for reference from other files.  */
+
+#define ASM_GLOBALIZE_LABEL(FILE,NAME) \
+  do { fprintf (FILE, "%s ", GLOBAL_ASM_OP);           \
+       assemble_name (FILE, NAME);                     \
+       fputs ("\n", FILE);} while (0)
+
+/* output external reference */
+#define ASM_OUTPUT_EXTERNAL(FILE,DECL,NAME) \
+  {fputs ("\t; extern\t", FILE); \
+  assemble_name (FILE, NAME); \
+  fputs ("\n", FILE);}
+
+
+
+/* Store in OUTPUT a string (made with alloca) containing
+   an assembler-name for a local static variable named NAME.
+   LABELNO is an integer which is different for each call.  */
+
+#define ASM_FORMAT_PRIVATE_NAME(OUTPUT, NAME, LABELNO) \
+( (OUTPUT) = (char *) alloca (strlen ((NAME)) + 10),   \
+  sprintf ((OUTPUT), "%s.%d", (NAME), (LABELNO)))
+
+/* How to refer to registers in assembler output.  This sequence is indexed
+   by compiler's hard-register-number (see above).  */
+#define REGISTER_NAMES                                         \
+{ "x", "d", "y", "sp", "pc", "a", "b", "ccr", "z",             \
+  "*_.frame", "*_.tmp", "*_.z", "*_.xy", "*fake clobber",      \
+  SOFT_REG_NAMES, "*sframe", "*ap"}
+
+
+/* Output a float value (represented as a C double) as an immediate operand.
+   This macro is a 68k-specific macro.  */
+
+#define ASM_OUTPUT_FLOAT_OPERAND(CODE,FILE,VALUE)              \
+ do {                                                          \
+      long l;                                                  \
+      REAL_VALUE_TO_TARGET_SINGLE (VALUE, l);                  \
+      asm_fprintf ((FILE), "%I0x%lx", l);                      \
+     } while (0)
+
+/* Output a double value (represented as a C double) as an immediate operand.
+   This macro is a 68k-specific macro.  */
+#define ASM_OUTPUT_DOUBLE_OPERAND(FILE,VALUE)                          \
+ do { char dstr[30];                                                   \
+      REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr);                    \
+      asm_fprintf (FILE, "%I0r%s", dstr);                              \
+    } while (0)
+
+/* Note, long double immediate operands are not actually
+   generated by m68k.md.  */
+#define ASM_OUTPUT_LONG_DOUBLE_OPERAND(FILE,VALUE)                     \
+ do { char dstr[30];                                                   \
+      REAL_VALUE_TO_DECIMAL (VALUE, "%.20g", dstr);                    \
+      asm_fprintf (FILE, "%I0r%s", dstr);                              \
+    } while (0)
+
+/* Print an instruction operand X on file FILE. CODE is the code from the
+   %-spec for printing this operand. If `%z3' was used to print operand
+   3, then CODE is 'z'.  */
+
+#define PRINT_OPERAND(FILE, X, CODE) \
+  print_operand (FILE, X, CODE)
+
+/* Print a memory operand whose address is X, on file FILE. */
+#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \
+  print_operand_address (FILE, ADDR)
+
+/* This is how to output an insn to push/pop a register on the stack.
+   It need not be very fast code.  
+
+   Don't define because we don't know how to handle that with
+   the STATIC_CHAIN_REGNUM (soft register).  Saving the static
+   chain must be made inside FUNCTION_PROFILER.  */
+
+#undef ASM_OUTPUT_REG_PUSH
+#undef ASM_OUTPUT_REG_POP
+
+/* This is how to output an element of a case-vector that is relative. */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \
+  asm_fprintf (FILE, "\t%s\tL%d-L%d\n", ASM_SHORT, VALUE, REL)
+
+/* This is how to output an element of a case-vector that is absolute. */
+#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \
+  asm_fprintf (FILE, "\t%s\t.L%d\n", ASM_SHORT, VALUE)
+
+/* This is how to output an assembler line that says to advance the
+   location counter to a multiple of 2**LOG bytes.  */
+#define ASM_OUTPUT_ALIGN(FILE,LOG)                     \
+  do {                                                  \
+      if ((LOG) > 1)                                    \
+          asm_fprintf ((FILE), "\t%s\n", ALIGN_ASM_OP); \
+  } while (0)
+
+\f
+/* Assembler Commands for Exception Regions.  */
+
+/* Default values provided by GCC should be ok. Assumming that DWARF-2
+   frame unwind info is ok for this platform. */
+
+/* How to renumber registers for dbx and gdb. */
+#define DBX_REGISTER_NUMBER(REGNO) \
+ ((REGNO))
+
+#undef PREFERRED_DEBUGGING_TYPE
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+/* The prefix for local labels.  You should be able to define this as
+   an empty string, or any arbitrary string (such as ".", ".L%", etc)
+   without having to make any other changes to account for the specific
+   definition.  Note it is a string literal, not interpreted by printf
+   and friends. */
+#define LOCAL_LABEL_PREFIX "."
+
+/* The prefix for immediate operands.  */
+#define IMMEDIATE_PREFIX "#"
+#define GLOBAL_ASM_OP   ".globl"
+#define ASM_LONG        ".long"
+#define ASM_SHORT       ".word"
+
+\f
+/* Miscellaneous Parameters.  */
+
+/* Define the codes that are matched by predicates in m68hc11.c.  */
+#define PREDICATE_CODES \
+{"stack_register_operand",   {SUBREG, REG}},                           \
+{"d_register_operand",       {SUBREG, REG}},                           \
+{"hard_addr_reg_operand",    {SUBREG, REG}},                           \
+{"hard_reg_operand",         {SUBREG, REG}},                           \
+{"m68hc11_logical_operator", {AND, IOR, XOR}},                         \
+{"m68hc11_arith_operator",   {AND, IOR, XOR, PLUS, MINUS,              \
+                             ASHIFT, ASHIFTRT, LSHIFTRT,               \
+                             ROTATE, ROTATERT }},                      \
+{"m68hc11_non_shift_operator", {AND, IOR, XOR, PLUS, MINUS}},          \
+{"m68hc11_unary_operator",   {NEG, NOT, SIGN_EXTEND, ZERO_EXTEND}},    \
+{"non_push_operand",         {SUBREG, REG, MEM}},                      \
+{"reg_or_some_mem_operand",  {SUBREG, REG, MEM}},                      \
+{"tst_operand",              {SUBREG, REG, MEM}},                      \
+{"cmp_operand",              {SUBREG, REG, MEM, SYMBOL_REF, LABEL_REF, \
+                            CONST_INT, CONST_DOUBLE}},
+
+/* Specify the machine mode that this machine uses
+   for the index in the tablejump instruction.  */
+#define CASE_VECTOR_MODE       Pmode
+
+/* Specify the tree operation to be used to convert reals to integers. */
+#define IMPLICIT_FIX_EXPR      FIX_ROUND_EXPR
+
+/* This flag, if defined, says the same insns that convert to a signed fixnum
+   also convert validly to an unsigned one.  */
+#define FIXUNS_TRUNC_LIKE_FIX_TRUNC
+
+/* This is the kind of divide that is easiest to do in the general case.  */
+#define EASY_DIV_EXPR          TRUNC_DIV_EXPR
+
+/* Max number of bytes we can move from memory to memory in one
+   reasonably fast instruction.  */
+#define MOVE_MAX               2
+
+/* MOVE_RATIO is the number of move instructions that is better than a
+   block move.  Make this small on 6811, since the code size grows very
+   large with each move.  */
+#define MOVE_RATIO             3
+
+/* Define if shifts truncate the shift count which implies one can omit
+   a sign-extension or zero-extension of a shift count.  */
+#define SHIFT_COUNT_TRUNCATED  1
+
+/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits
+   is done just by pretending it is already truncated.  */
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+/* Specify the machine mode that pointers have. After generation of rtl, the
+   compiler makes no further distinction between pointers and any other
+   objects of this machine mode.  */
+#define Pmode                  HImode
+
+/* A function address in a call instruction is a byte address (for indexing
+   purposes) so give the MEM rtx a byte's mode.  */
+#define FUNCTION_MODE          QImode
+
+/* define SCCS_DIRECTIVE if SCCS directives should be ignored */
+#define SCCS_DIRECTIVE         1
+
+/* Allow $ in identifiers */
+#define DOLLARS_IN_IDENTIFIERS 1
+
+/* Machine-dependent reorg pass.
+   Specific optimizations are defined here:
+    - this pass changes the Z register into either X or Y
+      (it preserves X/Y previous values in a memory slot in page0). 
+
+   When this pass is finished, the global variable
+   'z_replacement_completed' is set to 2.  */
+#define MACHINE_DEPENDENT_REORG(X)     m68hc11_reorg (X)
+
+extern int debug_m6811;
+extern int z_replacement_completed;
+extern int current_function_interrupt;
+extern int current_function_trap;
+
+#if GCC_VERSION == 2095
+extern rtx_ptr iy_reg;
+extern rtx_ptr iy_reg;
+extern rtx_ptr d_reg;
+extern rtx_ptr m68hc11_soft_tmp_reg;
+extern rtx_ptr m68hc11_compare_op0;
+extern rtx_ptr m68hc11_compare_op1;
+extern long m68hc11_min_offset;
+extern long m68hc11_max_offset;
+#endif
diff --git a/gcc/config/m68hc11/m68hc11.md b/gcc/config/m68hc11/m68hc11.md
new file mode 100644 (file)
index 0000000..b833069
--- /dev/null
@@ -0,0 +1,5840 @@
+;;- Machine description file for Motorola 68HC11 and 68HC12.
+;;- Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+;;- Contributed by Stephane Carrez (stcarrez@worldnet.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:
+;;
+;; T    an operand that can be accessed with 68HC1X direct addressing
+;;      mode.  For 68HC11 this includes the pseudo soft registers and
+;;      any memory operand that is a direct addressing (.page0).
+;;
+;;
+;; 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 directory (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}.
+;;
+
+;;--------------------------------------------------------------------
+;;-  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]))
+     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.
+;; The value is saved in memory and we test the low part only.
+;;
+(define_split
+  [(set (cc0)
+       (match_operand:QI 0 "hard_addr_reg_operand" "xy"))]
+  "z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
+  [(set (match_dup 3) (match_dup 2))
+   (set (cc0) (match_dup 4))]
+  "operands[2] = gen_rtx (REG, HImode, REGNO (operands[0]));
+   operands[3] = gen_rtx (REG, HImode, SOFT_TMP_REGNUM);
+   operands[4] = gen_rtx (REG, QImode, SOFT_TMP_REGNUM);")
+
+(define_insn "tstqi_1"
+  [(set (cc0)
+       (match_operand:QI 0 "tst_operand" "d,m,*A,!u"))]
+  ""
+  "@
+   tstb
+   tst\\t%0
+   #
+   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" "m"))
+   (use (match_operand:HI 1 "hard_reg_operand" "dxy"))
+   (use (reg:HI 11))]
+  "z_replacement_completed == 2"
+  [(set (mem:HI (pre_dec:HI (reg:HI 3))) (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 3))))]
+  "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" "dxy")
+                (match_operand:HI 1 "hard_reg_operand" "Aw")))]
+  "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"
+  [(set (cc0)
+       (compare (match_operand:HI 0 "tst_operand" 
+                               "dxy,?xy,d,dxy,dxy,dxy")
+                (match_operand:HI 1 "cmp_operand"
+                               "i,m,m,?*d*A,?u,!*w")))]
+  ""
+  "*
+{
+  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 "hard_reg_operand" "dxy")
+                (match_operand:HI 1 "cmp_operand" "m")))
+   (use (match_operand:HI 2 "hard_reg_operand" "dxy"))
+   (use (reg:HI 11))]
+  ""
+  "@
+   #")
+  
+(define_split /* "cmphi_z_used" */
+  [(set (cc0)
+       (compare (match_operand:HI 0 "hard_reg_operand" "dxy")
+                (match_operand:HI 1 "cmp_operand" "m")))
+   (use (match_operand:HI 2 "hard_reg_operand" "dxy"))
+   (use (reg:HI 11))]
+  "z_replacement_completed == 2"
+  [(set (mem:HI (pre_dec:HI (reg:HI 3))) (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 3))))]
+  "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" "xy")
+                (match_operand:QI 1 "cmp_operand" "uimA")))]
+  "z_replacement_completed == 2 && GET_MODE (operands[0]) == QImode"
+  [(parallel [(set (reg:HI 1) (match_dup 3))
+              (set (match_dup 3) (reg:HI 1))])
+   (set (cc0)
+        (compare (reg:QI 1) (match_dup 1)))
+   (parallel [(set (reg:HI 1) (match_dup 3))
+              (set (match_dup 3) (reg:HI 1))])]
+  "operands[3] = gen_rtx (REG, HImode, REGNO (operands[0]));")
+
+(define_split
+  [(set (cc0)
+       (compare (match_operand:QI 0 "hard_reg_operand" "dxy")
+                (match_operand:QI 1 "hard_reg_operand" "dxy")))]
+  "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 "cmpqi_1"
+  [(set (cc0)
+       (compare (match_operand:QI 0 "tst_operand" "d,d,*x*y,*x*y")
+                (match_operand:QI 1 "cmp_operand" "im,?u,?u,?dim*x*y")))]
+  ""
+  "@
+   cmpb\\t%1
+   cmpb\\t%b1
+   #
+   #")
+
+(define_insn "cmpqi_z_used"
+  [(set (cc0)
+       (compare (match_operand:QI 0 "hard_reg_operand" "dxy")
+                (match_operand:QI 1 "cmp_operand" "m")))
+   (use (match_operand:HI 2 "hard_reg_operand" "dxy"))
+   (use (reg:HI 11))]
+  ""
+  "@
+   #")
+  
+(define_split /* cmpqi_z_used */
+  [(set (cc0)
+       (compare (match_operand:QI 0 "hard_reg_operand" "dxy")
+                (match_operand:QI 1 "cmp_operand" "m")))
+   (use (match_operand:HI 2 "hard_reg_operand" "dxy"))
+   (use (reg:HI 11))]
+  "z_replacement_completed == 2"
+  [(set (mem:HI (pre_dec:HI (reg:HI 3))) (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 3))))]
+  "operands[3] = gen_rtx (REG, HImode, SOFT_Z_REGNUM);")
+
+(define_expand "cmpdf"
+  [(set (cc0)
+       (compare (match_operand:DF 0 "general_operand" "")
+                (match_operand:DF 1 "general_operand" "")))]
+  "0"
+  "
+{
+  m68hc11_compare_op0 = operands[0];
+  m68hc11_compare_op1 = operands[1];
+  DONE;
+}")
+
+(define_expand "cmpsf"
+  [(set (cc0)
+       (compare (match_operand:SF 0 "general_operand" "")
+                (match_operand:SF 1 "general_operand" "")))]
+  "0"
+  "
+{
+  m68hc11_compare_op0 = operands[0];
+  m68hc11_compare_op1 = operands[1];
+  DONE;
+}")
+
+;;--------------------------------------------------------------------
+;;-  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" "+um,+D,+D"))
+       (match_operand:SI 1 "general_operand" "D,Dim,u"))]
+  "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,+d,+d"))
+       (match_operand:HI 1 "general_operand" "d,dim,u"))]
+  ""
+  "#")
+
+(define_split
+  [(set (strict_low_part (match_operand:HI 0 "non_push_operand" "+um,+d,+d"))
+       (match_operand:HI 1 "general_operand" "d,dim,u"))]
+  "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,+d,+d"))
+       (match_operand:QI 1 "general_operand" "d,dim,u"))]
+  ""
+  "#")
+
+(define_split
+  [(set (strict_low_part (match_operand:QI 0 "non_push_operand" "+mu,+d,+d"))
+       (match_operand:QI 1 "general_operand" "d,dim,u"))]
+  "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 becuse
+;; 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_insn "movdi"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=U,!u,U,m,m,!u")
+       (match_operand:DI 1 "general_operand" "iU,iU,!u,mi,!u,!mu"))
+   (clobber (match_scratch:HI 2 "=&d,&d,&d,&d,&d,&d"))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=uUm")
+       (match_operand:DI 1 "general_operand" "iuUm"))
+   (clobber (match_scratch:HI 2 "=&d"))]
+  "reload_completed"
+  [(const_int 0)]
+  "m68hc11_split_move (operands[0], operands[1], operands[2]);
+   DONE;")
+
+(define_insn "movdf"
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=U,!u,U,m,m,!u")
+       (match_operand:DF 1 "general_operand" "iU,iU,!u,mi,!u,!mu"))
+   (clobber (match_scratch:HI 2 "=&d,&d,&d,&d,&d,&d"))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=uUm")
+       (match_operand:DF 1 "general_operand" "iuUm"))
+   (clobber (match_scratch:HI 2 "=&d"))]
+  "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_insn "movsi"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=mu,?D,m,?D,?u,?u,!u,D")
+       (match_operand:SI 1 "general_operand"      "imu,im,?D,!u,?D,mi,!u,!D"))
+   (clobber (match_scratch:HI 2                    "=&d,X,X,X,X,&d,&d,X"))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=m,D,m,D,!u,!u,!u,D")
+       (match_operand:SI 1 "general_operand" "im,im,D,!u,D,mi,!u,!D"))
+   (clobber (match_scratch:HI 2 "=&d,X,X,X,X,&d,&d,X"))]
+  "reload_completed"
+  [(const_int 0)]
+  "m68hc11_split_move (operands[0], operands[1], operands[2]);
+   DONE;")
+
+(define_insn "movsf"
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=m,D,m,D,!u,!u,!u,D")
+       (match_operand:SF 1 "general_operand" "im,im,D,!u,D,mi,!u,!D"))
+   (clobber (match_scratch:HI 2 "=&d,X,X,X,X,&d,&d,X"))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=m,D,m,D,!u,!u,!u,D")
+       (match_operand:SF 1 "general_operand" "im,im,D,!u,D,mi,!u,!D"))
+   (clobber (match_scratch:HI 2 "=&d,X,X,X,X,&d,&d,X"))]
+  "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"))]
+  "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]);
+        }
+    }
+}")
+
+(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" "=dAwuU,dAwu,m")
+       (match_operand:HI 1 "general_operand" "ruUi,m,dAwu"))]
+  "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,!um,*A,!*q")
+       (const_int 0))]
+  ""
+  "@
+   clrb
+   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" "=A")
+        (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 1) (match_dup 2))
+              (set (match_dup 2) (reg:HI 1))])
+   (set (reg:QI 1) (match_dup 1))
+   (parallel [(set (reg:HI 1) (match_dup 2))
+              (set (match_dup 2) (reg:HI 1))])]
+  "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" "=A"))]
+  "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 1) (match_dup 2))
+              (set (match_dup 2) (reg:HI 1))])
+   (set (match_dup 0) (reg:QI 1))
+   (parallel [(set (reg:HI 1) (match_dup 2))
+              (set (match_dup 2) (reg:HI 1))])]
+  "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]);
+        }
+    }
+}")
+
+(define_insn "*movqi_68hc12"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=d*AuU*q,d*A*q,d*A*q,m,m")
+       (match_operand:QI 1 "general_operand" "rui*q,U,m,d*q,!A"))]
+  "TARGET_M6812"
+  "*
+{
+  m68hc11_gen_movqi (insn, operands);
+  return \"\";
+}")
+
+(define_insn "*movqi_m68hc11"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=dA*q,m,m,dA*q,*u")
+       (match_operand:QI 1 "general_operand" "dAim*q,d*q,!A,*u,dA*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,&dB,&dB"))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:DI 0 "push_operand" "=<")
+       (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "dmu*B")))
+   (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" "=mu")
+       (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "dmu*B")))
+   (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,!u,!u")
+       (zero_extend:DI 
+           (match_operand:HI 1 "nonimmediate_operand" "m,dA,!u,dmA,!u")))
+   (clobber (match_scratch:HI 2 "=&d,&dB,&dB,&dB,&dB"))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:DI 0 "non_push_operand" "=m,m,m,!u,!u")
+       (zero_extend:DI 
+           (match_operand:HI 1 "nonimmediate_operand" "m,dA,!u,dmA,!u")))
+   (clobber (match_scratch:HI 2 "=&d,&dB,&dB,&dB,&dB"))]
+  "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,&dB,d"))]
+  ""
+  "#")
+
+(define_split 
+  [(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,&dB,d"))]
+  "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,mu,m,m,!u,!u")
+        (zero_extend:SI 
+           (match_operand:HI 1 "nonimmediate_operand" "dAmu,dA,m,!u,m,!u")))
+   (clobber (match_scratch:HI 2 "=X,X,&d,&dB,&dB,&dB"))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:SI 0 "non_push_operand" "=D,mu,m,m,!u,!u")
+       (zero_extend:SI 
+           (match_operand:HI 1 "nonimmediate_operand" "dAmu,dA,m,!u,m,!u")))
+   (clobber (match_scratch:HI 2 "=X,X,&d,&dB,&dB,&dB"))]
+  "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,mu")
+      (zero_extend:SI 
+         (match_operand:QI 1 "nonimmediate_operand" "dxymu,dxy")))]
+  ""
+  "#")
+
+(define_split 
+  [(set (match_operand:SI 0 "non_push_operand" "=mu")
+       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "dxy")))]
+  "reload_completed && !X_REG_P (operands[0])"
+  [(set (match_dup 2) (match_dup 3))
+   (set (match_dup 4) (const_int 0))
+   (set (match_dup 5) (const_int 0))]
+  "
+   operands[2] = m68hc11_gen_lowpart (HImode, operands[0]);
+   operands[3] = gen_rtx (REG, HImode, REGNO (operands[1]));
+   operands[4] = m68hc11_gen_lowpart (HImode, operands[0]);
+   operands[4] = m68hc11_gen_highpart (QImode, operands[4]);
+   operands[5] = m68hc11_gen_highpart (HImode, operands[0]);")
+
+(define_split 
+  [(set (match_operand:SI 0 "hard_reg_operand" "=D")
+       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "dxymu")))]
+  "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")))]
+  ""
+ "*
+{
+  rtx ops[2];
+
+  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,mu")
+       (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "dmux,d")))]
+  ""
+  "*
+{
+  extern rtx ix_reg;
+  rtx ops[3];
+  int need_tst = 0;
+
+  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,u*x*ym")
+       (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "dum,0")))]
+  ""
+  "*
+{
+  rtx ops[2];
+
+  if (A_REG_P (operands[0]))
+    return \"#\";
+
+  ops[0] = gen_label_rtx ();
+  if (D_REG_P (operands[0]))
+    {
+      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" "=D")
+       (sign_extend:SI (match_operand:HI 1 "register_operand" "A")))]
+  "reload_completed && (Y_REG_P (operands[1]) || Z_REG_P (operands[1]))"
+  [(set (reg:HI 1) (match_dup 1))
+   (set (match_dup 0) (sign_extend:SI (reg:HI 1)))]
+  "")
+
+(define_insn "extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "=D,D")
+       (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "dm,!uA")))]
+  ""
+  "*
+{
+  extern rtx ix_reg;
+  rtx ops[1];
+  int x_reg_used;
+
+  if (Y_REG_P (operands[1]))
+    return \"#\";
+
+  ops[0] = gen_label_rtx ();
+
+  if (X_REG_P (operands[1]))
+    {
+      output_asm_insn (\"xgdx\", operands);
+      x_reg_used = 1;
+    }
+  else
+    {
+      /* X can be used as a 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)
+        {
+          output_asm_insn (\"ldd\\t%1\", operands);
+        }
+    }
+  output_asm_insn (\"ldx\\t#0\", operands);
+  if (D_REG_P (operands[1]) || x_reg_used)
+    {
+      output_asm_insn (\"tsta\", operands);
+    }
+  else
+    {
+      output_asm_insn (\"ldd\\t%1\", operands);
+    }
+  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]));
+
+  CC_STATUS_INIT;
+  return \"\";
+}")
+
+
+;;--------------------------------------------------------------------
+;;- 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 "register_operand" "")
+                             (match_operand:SI 2 "general_operand" "")))
+              (clobber (match_scratch:HI 3 ""))])]
+  ""
+  "")
+
+;;
+;; Translate D = D + D into D = D << 1
+;; We have to do this because adding a register to itself is not possible.
+;;
+;; Manipulation of A and B registers directly confuses the cse-regs pass
+;; so the split must be made after z-replacement register.
+;;
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "=D")
+       (plus:SI (match_dup 0)
+                (match_dup 0)))
+   (clobber (match_scratch:HI 1 "=X"))]
+  "reload_completed && z_replacement_completed == 2"
+  [(set (reg:HI 1) (ashift:HI (reg:HI 1) (const_int 1)))
+   (parallel [(set (reg:HI 1) (reg:HI 0))
+              (set (reg:HI 0) (reg:HI 1))])
+   (set (reg:QI 6) (rotate:QI (reg:QI 6) (reg:QI 7)))
+   (set (reg:QI 5) (rotate:QI (reg:QI 5) (reg:QI 7)))
+   (parallel [(set (reg:HI 1) (reg:HI 0))
+              (set (reg:HI 0) (reg:HI 1))])]
+  "")
+
+
+(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" "=D,D")
+       (plus:SI (zero_extend:SI 
+                  (match_operand:QI 1 "general_operand" "dAmi,!dAmiu"))
+                (match_operand:SI 2 "memory_operand" "m,m")))
+   (clobber (match_scratch:HI 3 "=X,X"))]
+  "reload_completed"
+  [(set (reg:HI 1) (zero_extend:HI (match_dup 1)))
+   (parallel [(set (match_dup 0) 
+                  (plus:SI (zero_extend:SI (reg:HI 1)) (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] = gen_rtx (CONST_INT, VOIDmode, 0);
+    }
+  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" "=m,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,L,?miu,!D")))
+   (clobber (match_scratch:HI 3 "=d,X,d,X,X"))]
+  ""
+  "*
+{
+  rtx   ops[3];
+  char* add_insn;
+  char* inc_insn;
+  char* incb_mem;
+  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_rtx (CONST_INT, VOIDmode, - 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" "=D")
+       (plus:SI (match_operand:SI 1 "register_operand" "%0")
+                (match_operand:SI 2 "const_int_operand" "")))
+   (clobber (match_scratch:HI 3 "=X"))]
+  "reload_completed && z_replacement_completed == 2
+   && ((INTVAL (operands[2]) & 0x0FFFF) == 0)"
+  [(set (reg:HI 0) (plus:HI (reg:HI 0) (match_dup 3)))]
+  "operands[3] = m68hc11_gen_highpart (HImode, operands[2]);")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "=D")
+       (plus:SI (match_operand:SI 1 "register_operand" "%0")
+                (match_operand:SI 2 "general_operand" "mui")))
+   (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 1) (plus:HI (reg:HI 1) (match_dup 3)))
+   (parallel [(set (reg:HI 1) (reg:HI 0))
+              (set (reg:HI 0) (reg:HI 1))])
+   (set (reg:QI 6) (plus:QI (plus:QI (reg:QI 7) (reg:QI 6)) (match_dup 4)))
+   (set (reg:QI 5) (plus:QI (plus:QI (reg:QI 7) (reg:QI 5)) (match_dup 5)))
+   (parallel [(set (reg:HI 1) (reg:HI 0))
+              (set (reg:HI 0) (reg:HI 1))])]
+  "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 7)))]
+  ""
+  "*
+{
+  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_split /* "*addhi3_strict_low_part" */
+  [(set (strict_low_part (match_operand:QI 0 "register_operand" "+dxy"))
+        (plus:QI (match_operand:QI 1 "register_operand" "")
+                 (match_operand:QI 2 "general_operand" "")))]
+  "0 && z_replacement_completed == 2"
+  [(set (match_dup 0)
+       (plus:QI (match_dup 1) (match_dup 2)))]
+  "")
+
+(define_split /* "*addhi3_strict_low_part" */
+  [(set (match_operand:HI 0 "register_operand" "=dA")
+        (plus:HI (match_operand:HI 1 "register_operand" "%0")
+                 (match_operand:HI 2 "general_operand" "")))
+   (clobber (match_scratch:HI 3 ""))]
+  "0 && z_replacement_completed == 2 && !SP_REG_P (operands[0])"
+  [(set (match_dup 0)
+       (plus:HI (match_dup 1) (match_dup 2)))]
+  "")
+
+(define_insn "*addhi3_68hc12"
+  [(set (match_operand:HI 0 "register_operand" "=d,A*w,A*w")
+        (plus:HI (match_operand:HI 1 "register_operand" "%0,0,Aw")
+                 (match_operand:HI 2 "general_operand" "imA*wu,id,id")))]
+  "TARGET_M6812"
+  "*
+{
+  int val;
+  const char* insn_code;
+
+  if (D_REG_P (operands[0]))
+    {
+      if (X_REG_P (operands[2]))
+       {
+         output_asm_insn (\"xgdx\", operands);
+         output_asm_insn (\"leax\\td,%2\", operands);
+         return \"xgdx\";
+       }
+      else if (Y_REG_P (operands[2]))
+       {
+         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]))
+    {
+      cc_status = cc_prev_status;
+      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 (GET_CODE (operands[2]) == CONST_INT
+      && (val = INTVAL (operands[2])) != 0
+      && (CONST_OK_FOR_LETTER_P (val, 'P')
+         || (val > 0 && val <= 8)))
+    {
+      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);
+      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 IY and then to D register.
+     Register IY is lost, this is specified by the (clobber) statement.  */
+  output_asm_insn (\"ts%3\", operands);
+  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\";
+}")
+
+;;
+;; Translate d = d + d into d = 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:HI 0 "hard_reg_operand" "=dA")
+       (plus:HI (match_dup 0)
+                (match_dup 0)))]
+  "reload_completed"
+  [(set (match_dup 0) (ashift:HI (match_dup 0) (const_int 1)))]
+  "")
+
+(define_insn "*addhi3"
+  [(set (match_operand:HI 0 "hard_reg_operand" "=dA,d,!A,d*A,!d,!w")
+       (plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0,0,0")
+                (match_operand:HI 2 "general_operand" "N,i,I,umi*A*d,!*d*w,i")))]
+  "TARGET_M6811"
+  "*
+{
+  const char* insn_code;
+  int val;
+  extern rtx ix_reg;
+
+  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" "=dA")
+       (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" "=!*rm,dq*A")
+        (plus:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0")
+                 (match_operand:QI 2 "general_operand" "N,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 7)
+                          (match_operand:QI 2 "register_operand" "%0"))
+                 (match_operand:QI 3 "general_operand" "ium")))]
+  ""
+  "adc%0\\t%b3")
+
+;;--------------------------------------------------------------------
+;;- 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")
+       (minus:SI (match_operand:SI 1 "general_operand" "0,!mui")
+                 (match_operand:SI 2 "general_operand" "!mui,!D")))
+   (clobber (match_scratch:HI 3 "=X,X"))]
+  ""
+  "#")
+
+(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" "d!mui"))))
+   (clobber (match_scratch:HI 3 "=X"))]
+  ""
+  "*
+{
+  rtx ops[2];
+
+  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"))))
+   (clobber (match_scratch:HI 3 "=X"))]
+  ""
+  "*
+{
+  rtx ops[2];
+
+  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" "=D")
+       (minus:SI (match_operand:SI 1 "register_operand" "0")
+                 (match_operand:SI 2 "general_operand" "mui")))
+   (clobber (match_scratch:HI 3 "=X"))]
+  "reload_completed && z_replacement_completed == 2
+   && X_REG_P (operands[1])"
+  [(set (reg:HI 1) (minus:HI (reg:HI 1) (match_dup 3)))
+   (parallel [(set (reg:HI 0) (reg:HI 1))
+              (set (reg:HI 1) (reg:HI 0))])
+   (set (reg:QI 6) (minus:QI (minus:QI (reg:QI 7) (reg:QI 6)) (match_dup 4)))
+   (set (reg:QI 5) (minus:QI (minus:QI (reg:QI 7) (reg:QI 5)) (match_dup 5)))
+   (parallel [(set (reg:HI 0) (reg:HI 1))
+              (set (reg:HI 1) (reg:HI 0))])]
+  "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" "=D")
+       (minus:SI (match_operand:SI 1 "general_operand" "mui")
+                 (match_operand:SI 2 "register_operand" "D")))
+   (clobber (match_scratch:HI 3 "=X"))]
+  "reload_completed && z_replacement_completed == 2
+   && X_REG_P (operands[2])"
+  [(set (reg:HI 1) (minus:HI (reg:HI 1) (match_dup 3)))
+   (parallel [(set (reg:HI 0) (reg:HI 1))
+              (set (reg:HI 1) (reg:HI 0))])
+   (set (reg:QI 6) (minus:QI (minus:QI (reg:QI 7) (reg:QI 6)) (match_dup 4)))
+   (set (reg:QI 5) (minus:QI (minus:QI (reg:QI 7) (reg:QI 5)) (match_dup 5)))
+   (parallel [(set (reg:HI 0) (reg:HI 1))
+              (set (reg:HI 1) (reg:HI 0))])
+   (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]);")
+
+;;
+;; - 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")))]
+  ""
+  "
+{
+  if (TARGET_M6811 && SP_REG_P (operands[0]))
+    {
+     emit_insn (gen_rtx (PARALLEL, VOIDmode, gen_rtvec (2,
+                        gen_rtx (SET, VOIDmode,
+                                 operand0,
+                                 gen_rtx (MINUS, HImode,
+                                          operand1, operand2)),
+                       gen_rtx (CLOBBER, VOIDmode,
+                               gen_rtx (SCRATCH, HImode, 0)))));
+     DONE;
+  }
+}")
+
+;;
+;; 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" "uim*d,!*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]))
+    {
+      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);
+      return \"xgdx\";
+    }
+
+  /* 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")
+       (minus:HI (match_operand:HI 1 "register_operand" "0,0")
+                 (match_operand:HI 2 "general_operand" "uim*A*d,uim*d*A")))]
+  ""
+  "*
+{
+  /* 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")
+                  (match_operand:QI 2 "general_operand" "uim*x*y*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 7)
+                            (match_operand:QI 2 "register_operand" "0"))
+                  (match_operand:QI 3 "general_operand" "ium")))]
+  ""
+  "sbc%0\\t%b3")
+
+;;--------------------------------------------------------------------
+;;- Multiply instructions.
+;;--------------------------------------------------------------------
+;;
+;; 32 and 64-bit multiply are handled by the library
+;;
+
+(define_insn "mulhi3"
+  [(set (match_operand:HI 0 "register_operand" "=d")
+       (mult:HI (match_operand:HI 1 "register_operand" "%0")
+                (match_operand:HI 2 "register_operand" "x")))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+  /* D * X -> D  (X and Y are preserved by this function call).  */
+  return \"jsr\\t___mulhi3\";
+}")
+
+(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")
+        (mult:QI (match_operand:QI 1 "nonimmediate_operand" "dum")
+                (match_operand:QI 2 "nonimmediate_operand" "dum")))]
+  ""
+  "*
+{
+  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_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);
+       }
+    }
+  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" "=mu")
+       (and:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu")
+               (match_operand:DI 2 "general_operand" "imu")))
+   (clobber (match_scratch:HI 3 "=d"))]
+  ""
+  "#")
+
+(define_insn "andsi3"
+  [(set (match_operand:SI 0 "register_operand" "=D")
+       (and:SI (match_operand:SI 1 "register_operand" "%0")
+               (match_operand:SI 2 "general_operand" "Dimu")))]
+  ""
+  "#")
+
+(define_insn "andhi3"
+  [(set (match_operand:HI 0 "register_operand" "=d,!u,d,!*A")
+       (and:HI (match_operand:HI 1 "register_operand" "%0,0,0,0")
+               (match_operand:HI 2 "general_operand" "i,i,!um*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;
+      char lowpart_zero     = 0;
+      char lowpart_unknown  = 0;
+      char highpart_zero    = 0;
+      char highpart_unknown = 0;
+
+      if (val == 0xFFFF)
+       {
+         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_rtx (CONST_INT, VOIDmode, (~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_rtx (CONST_INT, VOIDmode, ((~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 \"\";
+    }
+
+  CC_STATUS_INIT;
+  return \"andb\\t%b2\\n\\tanda\\t%h2\";
+}")
+
+(define_insn "andqi3"
+  [(set (match_operand:QI 0 "register_operand" "=d,!u,d,d,?*A,?*A,!*q")
+        (and:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,0,0")
+             (match_operand:QI 2 "general_operand" "i,i,!um,?*A,i!um,?*A,i!um*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 == 0xFF)
+       {
+         cc_status = cc_prev_status;
+         return \"\";
+       }
+      if (val == 0)
+       {
+          if (D_REG_P (operands[0]) || DB_REG_P (operands[0]))
+           return \"clrb\";
+         else if (DA_REG_P (operands[0]))
+           return \"clra\";
+         else
+           return \"clr\\t%b0\";
+       }
+      if (!H_REG_P (operands[0]))
+        {
+          rtx ops[2];
+          ops[0] = operands[0];
+          ops[1] = gen_rtx (CONST_INT, VOIDmode, (~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);
+}")
+
+;;--------------------------------------------------------------------
+;;- Bit set or instructions.
+;;--------------------------------------------------------------------
+
+(define_insn "iordi3"
+  [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=mu")
+       (ior:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu")
+               (match_operand:DI 2 "general_operand" "imu")))
+   (clobber (match_scratch:HI 3 "=d"))]
+  ""
+  "#")
+
+(define_insn "iorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=D")
+       (ior:SI (match_operand:SI 1 "register_operand" "%0")
+               (match_operand:SI 2 "general_operand" "Dimu")))]
+  ""
+  "#")
+
+(define_insn "iorhi3"
+  [(set (match_operand:HI 0 "register_operand" "=d,!u,d,!*A")
+       (ior:HI (match_operand:HI 1 "register_operand" "%0,0,0,0")
+               (match_operand:HI 2 "general_operand" "i,i,!um*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)
+        {
+          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 \"\";
+    }
+
+  CC_STATUS_INIT;
+  return \"orab\\t%b2\\n\\toraa\\t%h2\";
+}")
+
+(define_insn "iorqi3"
+  [(set (match_operand:QI 0 "register_operand" "=d,!u,d,d,?*A,?*A,!*q")
+       (ior:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0,0,0")
+            (match_operand:QI 2 "general_operand" "i,i,!um,!*A,i!um,!*A,i!um*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 (!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);
+}")
+
+;;--------------------------------------------------------------------
+;;- xor instructions.
+;;--------------------------------------------------------------------
+
+(define_insn "xordi3"
+  [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=mu")
+       (xor:DI (match_operand:DI 1 "reg_or_some_mem_operand" "%imu")
+               (match_operand:DI 2 "general_operand" "imu")))
+   (clobber (match_scratch:HI 3 "=d"))]
+  ""
+  "#")
+
+(define_insn "xorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=D")
+       (xor:SI (match_operand:SI 1 "register_operand" "%0")
+               (match_operand:SI 2 "general_operand" "Dimu")))]
+  ""
+  "#")
+
+(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 \"eorb\\t%b2\\n\\teora\\t%h2\";
+}")
+
+(define_insn "xorqi3"
+  [(set (match_operand:QI 0 "register_operand" "=d,d,!*u*A,!*u*A,!*q")
+        (xor:QI (match_operand:QI 1 "register_operand" "%0,0,0,0,0")
+             (match_operand:QI 2 "general_operand" "ium,!*A,ium,!*A,ium*A*u")))]
+  ""
+  "*
+{
+  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,D")
+       (match_operator:SI 3 "m68hc11_logical_operator"
+               [(zero_extend:SI
+                    (match_operand:HI 1 "general_operand" "imdA,!udimA"))
+                (match_operand:SI 2 "general_operand" "Dimu,!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
+  [(set (match_operand:SI 0 "register_operand" "=D,D")
+       (match_operator:SI 3 "m68hc11_logical_operator"
+                [(zero_extend:SI
+                    (match_operand:QI 1 "general_operand" "dxy,imu"))
+                 (match_operand:SI 2 "general_operand" "imuD,imuD")]))]
+  "z_replacement_completed == 2"
+  [(set (reg:QI 5) (match_dup 4))
+   (set (reg:QI 1) (match_dup 7))
+   (set (reg:QI 6) (match_op_dup 3 [(reg:QI 6) (match_dup 5)]))
+   (set (reg:HI 0) (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]);
+     }       
+   ")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "=D,D")
+       (match_operator:SI 3 "m68hc11_logical_operator"
+                [(zero_extend:SI
+                    (match_operand:HI 1 "general_operand" "dA,imu"))
+                 (match_operand:SI 2 "general_operand" "imuD,imuD")]))]
+  "reload_completed"
+  [(set (reg:HI 1) (match_dup 4))
+   (set (reg:HI 1) (match_op_dup 3 [(reg:HI 1) (match_dup 5)]))
+   (set (reg:HI 0) (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]);
+     }       
+   ")
+
+(define_insn "*logicallhi3_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")
+       (match_operator:HI 3 "m68hc11_logical_operator"
+               [(zero_extend:HI
+                    (match_operand:QI 1 "general_operand" "imud"))
+                (match_operand:HI 2 "general_operand" "dimu")]))]
+  ""
+  "#")
+
+(define_split
+  [(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"))
+                (match_operand:HI 2 "general_operand" "dimu")]))]
+  "z_replacement_completed == 2"
+  [(set (reg:QI 6) (match_dup 6))
+   (set (reg:QI 5) (match_dup 4))
+   (set (reg:QI 6) (match_op_dup 3 [(reg:QI 6) (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];
+     }
+  ")
+
+(define_split
+  [(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))]))]
+  "z_replacement_completed == 2"
+  [(set (reg:QI 6) (match_dup 5))
+   (set (reg:QI 5) (match_dup 4))]
+  "
+   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")
+          (match_operator:SI 3 "m68hc11_logical_operator"
+             [(lshiftrt:SI 
+                  (match_operand:SI 1 "general_operand" "uim,!D")
+                  (const_int 16))
+               (match_operand:SI 2 "general_operand" "uim,0")]))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "=D,D")
+          (match_operator:SI 3 "m68hc11_logical_operator"
+               [(lshiftrt:SI 
+                       (match_operand:SI 1 "general_operand" "uim,!D")
+                       (const_int 16))
+                (match_operand:SI 2 "general_operand" "uim,0")]))]
+  "reload_completed"
+  [(set (reg:HI 1) (match_dup 4))
+   (set (reg:HI 1) (match_op_dup 3 [(reg:HI 1) (match_dup 5)]))
+   (set (reg:HI 0) (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);
+
+")
+
+(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
+  [(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")]))]
+  "z_replacement_completed == 2"
+  [(parallel [(set (reg:HI 1) (reg:HI 0))
+              (set (reg:HI 0) (reg:HI 1))])
+  (set (reg:HI 1) (match_op_dup 3 [(reg:HI 1) (match_dup 4)]))
+  (parallel [(set (reg:HI 1) (reg:HI 0))
+             (set (reg:HI 0) (reg:HI 1))])]
+  "operands[4] = m68hc11_gen_lowpart (HImode, operands[1]);
+   PUT_MODE (operands[3], HImode);")
+
+
+;;--------------------------------------------------------------------
+;;- 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 (AND, OR, XOR).
+(define_split
+  [(set (match_operand:DI 0 "reg_or_some_mem_operand" "=mu")
+       (match_operator:DI 4 "m68hc11_logical_operator"
+            [(match_operand:DI 1 "reg_or_some_mem_operand" "%imu")
+             (match_operand:DI 2 "general_operand" "imu")]))
+   (clobber (match_scratch:HI 3 "=d"))]
+  "reload_completed"
+  [(const_int 0)]
+  "m68hc11_split_logical (SImode, GET_CODE (operands[4]), operands);
+   DONE;")
+
+;; Split 32-bit logical operations (AND, OR, XOR).
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "=D")
+       (match_operator:SI 3 "m68hc11_logical_operator"
+            [(match_operand:SI 1 "register_operand" "%0")
+             (match_operand:SI 2 "general_operand" "Dimu")]))]
+  "reload_completed"
+  [(const_int 0)]
+  "m68hc11_split_logical (HImode, GET_CODE (operands[3]), 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" "=A")
+       (match_operator:HI 3 "m68hc11_arith_operator"
+            [(match_operand:HI 1 "hard_addr_reg_operand" "0")
+            (match_operand:HI 2 "general_operand" "dAuim")]))]
+  "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
+            || (GET_CODE (operands[2]) == CONST_INT
+               && INTVAL (operands[2]) >= -4
+               && INTVAL (operands[2]) <= 4)))"
+  [(set (match_dup 4) (match_dup 5))
+   (set (match_dup 8) (match_dup 7))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])
+   (set (reg:HI 1) (match_op_dup 3 [(reg:HI 1) (match_dup 6)]))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])]
+  "
+   /* Save the operand2 in a temporary location and use it. */
+   if (H_REG_P (operands[2])
+       || reg_mentioned_p  (operands[0], operands[2]))
+     {
+       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" "=A")
+       (match_operator:HI 3 "m68hc11_arith_operator"
+            [(match_operand:HI 1 "general_operand" "mu")
+            (match_operand:HI 2 "general_operand" "dAuim")]))]
+  "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
+            || (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 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])
+   (set (reg:HI 1) (match_op_dup 3 [(reg:HI 1) (match_dup 2)]))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])]
+  "
+   ")
+
+;;
+;; 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" "=d")
+       (match_operator:HI 3 "m68hc11_non_shift_operator"
+            [(match_operand:HI 1 "d_register_operand" "%0")
+            (match_operand:HI 2 "hard_reg_operand" "*d*A")]))]
+  "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);")
+
+;;--------------------------------------------------------------------
+;; 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" "=A")
+       (match_operator:HI 2 "m68hc11_unary_operator"
+            [(match_operand 1 "general_operand" "uim*d*A")]))]
+  "z_replacement_completed == 2"
+  [(set (match_dup 4) (match_dup 5))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])
+   (set (reg:HI 1) (match_op_dup 2 [(match_dup 3)]))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])]
+  "
+{
+  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" "=xy")
+       (match_operator:QI 3 "m68hc11_arith_operator"
+            [(match_operand:QI 1 "hard_addr_reg_operand" "%0")
+            (match_operand:QI 2 "general_operand" "dxyuim")]))]
+  "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 1) (match_dup 4))
+              (set (match_dup 4) (reg:HI 1))])
+   (set (reg:QI 1) (match_op_dup 3 [(reg:QI 1) (match_dup 7)]))
+   (parallel [(set (reg:HI 1) (match_dup 4))
+              (set (match_dup 4) (reg:HI 1))])]
+  "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" "=d")
+       (match_operator:QI 3 "m68hc11_non_shift_operator"
+            [(match_operand:QI 1 "d_register_operand" "%0")
+            (match_operand:QI 2 "hard_reg_operand" "*d*x*y")]))]
+  "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" "=xy")
+       (match_operator:QI 2 "m68hc11_unary_operator"
+            [(match_operand:QI 1 "general_operand" "uim*d*x*y")]))]
+  "z_replacement_completed == 2"
+  [(set (match_dup 4) (match_dup 5))
+   (parallel [(set (reg:HI 1) (match_dup 3))
+              (set (match_dup 3) (reg:HI 1))])
+   (set (reg:QI 1) (match_op_dup 2 [(match_dup 6)]))
+   (parallel [(set (reg:HI 1) (match_dup 3))
+              (set (match_dup 3) (reg:HI 1))])]
+  "
+{
+  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 "register_operand" "0")))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+
+  /* With -Os or without -O, use a special library call.  */
+  if (optimize_size || optimize == 0)
+    return \"bsr\\t___negsi2\";
+
+  /* 32-bit complement and add 1.  The comb/coma set the carry and they
+     are smaller (use it for low-part).  The eorb/eora leave the carry
+     unchanged but are bigger (use it for high-part).  */
+  output_asm_insn (\"comb\\n\\tcoma\\n\\taddd\\t#1\\n\\txgdx\", operands);
+  output_asm_insn (\"eorb\\t#0xFF\\n\\teora\\t#0xFF\", operands);
+  return \"adcb\\t#0\\n\\tadca\\t#0\\n\\txgdx\";
+}")
+
+(define_insn "neghi2"
+  [(set (match_operand:HI 0 "register_operand" "=d,d,*A")
+       (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,!um,!*A")
+       (neg:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0")))]
+  ""
+  "@
+   negb
+   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")
+       (not:SI (match_operand:SI 1 "general_operand" "0")))]
+  ""
+  "@
+   bsr\\t___one_cmplsi2")
+
+(define_insn "one_cmplhi2"
+  [(set (match_operand:HI 0 "non_push_operand" "=d,!um,*A")
+       (not:HI (match_operand:HI 1 "general_operand" "0,0,0")))]
+  ""
+  "@
+   comb\\n\\tcoma
+   com\\t%b0\\n\\tcom\\t%h0
+   #")
+
+(define_insn "one_cmplqi2"
+  [(set (match_operand:QI 0 "non_push_operand" "=d,!um,!*A")
+       (not:QI (match_operand:QI 1 "general_operand" "0,0,0")))]
+  ""
+  "@
+   comb
+   com\\t%b0
+   #")
+
+(define_split /* "*one_cmplsi2" */
+  [(set (match_operand:SI 0 "non_push_operand" "=Dum")
+       (not:SI (match_operand:SI 1 "non_push_operand" "0")))]
+  "z_replacement_completed == 2
+   && (!D_REG_P (operands[0]) || (optimize && optimize_size == 0))"
+  [(set (reg:HI 1) (not:HI (reg:HI 1)))
+   (parallel [(set (reg:HI 0) (reg:HI 1))
+              (set (reg:HI 1) (reg:HI 0))])
+   (set (reg:HI 1) (not:HI (reg:HI 1)))
+   (parallel [(set (reg:HI 0) (reg:HI 1))
+              (set (reg:HI 1) (reg:HI 0))])]
+  "
+{
+  /* The result pattern only works for D register.
+     Generate 2 one_cmplhi2 instructions. */
+  if (!D_REG_P (operands[0]))
+    {
+      rtx ops[2];
+
+      ops[0] = m68hc11_gen_lowpart (HImode, operands[0]);
+      ops[1] = m68hc11_gen_highpart (HImode, operands[0]);
+      emit_insn (gen_one_cmplhi2 (ops[0], ops[0]));
+      emit_insn (gen_one_cmplhi2 (ops[1], ops[1]));
+      DONE;
+    }
+}")
+
+;;--------------------------------------------------------------------
+;;- 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" "=<,um")
+       (ashift:DI (match_operand:DI 1 "general_operand" "umi,umi")
+                  (const_int 32)))
+   (clobber (match_scratch:HI 2 "=A,d"))]
+   ""
+   "#")
+
+(define_split
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=<,um")
+       (ashift:DI (match_operand:DI 1 "general_operand" "umi,umi")
+                  (const_int 32)))
+   (clobber (match_scratch:HI 2 "=A,d"))]
+   "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" "=um")
+       (ashift:DI (match_operand:DI 1 "general_operand" "umi")
+                  (const_int 1)))
+   (clobber (match_scratch:HI 2 "=d"))]
+   ""
+   "#")
+
+(define_split
+  [(set (match_operand:DI 0 "non_push_operand" "=um")
+       (ashift:DI (match_operand:DI 1 "general_operand" "umi")
+                  (const_int 1)))
+   (clobber (match_scratch:HI 2 "=d"))]
+   "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))
+    (set (match_dup 2) (rotate:HI (match_dup 2) (reg:HI 7)))
+    (set (match_dup 6) (match_dup 2))
+
+    (set (match_dup 2) (match_dup 7))
+    (set (match_dup 2) (rotate:HI (match_dup 2) (reg:HI 7)))
+    (set (match_dup 8) (match_dup 2))
+
+    (set (match_dup 2) (match_dup 9))
+    (set (match_dup 2) (rotate:HI (match_dup 2) (reg:HI 7)))
+    (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")
+          (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "uim")
+                               (const_int 16))
+                  (match_operand:SI 2 "general_operand" "0")))]
+  ""
+  "#")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "=D")
+          (plus:SI (lshiftrt:SI (match_operand:SI 1 "general_operand" "uim")
+                               (const_int 16))
+                  (match_operand:SI 2 "general_operand" "0")))]
+  "z_replacement_completed == 2"
+  [(set (reg:HI 1) (plus:HI (reg:HI 1) (match_dup 3)))
+   (set (reg:HI 0) (plus:HI (plus:HI (reg:HI 0) (const_int 0)) (reg:HI 7)))]
+  "operands[3] = m68hc11_gen_highpart (HImode, operands[1]);")
+
+(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" "=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 && reload_completed && z_replacement_completed == 2"
+  [(set (reg:HI 0) (plus:HI (reg:HI 0) (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" "=D")
+          (plus:SI (and:SI (match_operand:SI 1 "general_operand" "%uim")
+                          (const_int 65535))
+                  (match_operand:SI 2 "general_operand" "0")))]
+  "z_replacement_completed == 2"
+  [(set (reg:HI 1) (plus:HI (reg:HI 1) (match_dup 3)))
+   (set (reg:HI 0) (plus:HI (plus:HI (reg:HI 0) (const_int 0)) (reg:HI 7)))]
+  "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" "=D,um")
+       (ashift:SI (match_operand:SI 1 "general_operand" "Duim,D")
+                  (const_int 16)))
+   (clobber (match_scratch:HI 3 "=X,X"))]
+   ""
+  [(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,*um")
+       (ashift:SI (match_operand:SI 1 "general_operand" "Duim,D")
+                  (const_int 16)))
+   (clobber (match_scratch:HI 3 "=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 3 "=X"))]
+   ""
+   "#")
+
+(define_split /* "*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 3 "=X"))]
+   "reload_completed"
+   [(set (reg:HI 0) (match_dup 1))
+    (set (reg:HI 1) (const_int 0))]
+   "")
+
+(define_insn "*ashlsi3_const1"
+  [(set (match_operand:SI 0 "non_push_operand" "=D,D,*um,?*um")
+       (ashift:SI (match_operand:SI 1 "nonimmediate_operand" "0,*um,0,*um")
+                  (const_int 1)))
+   (clobber (match_scratch:HI 3 "=X,X,&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 2 "const_int_operand" "")))
+   (clobber (match_scratch:HI 3 "=y"))]
+   ""
+   "*
+{
+  CC_STATUS_INIT;
+  return \"ldy\\t%2\\n\\tbsr\\t___ashlsi3\";
+}")
+
+(define_insn "*ashlsi3"
+  [(set (match_operand:SI 0 "register_operand" "+D,D")
+       (ashift:SI (match_dup 0)
+                  (match_operand:HI 2 "general_operand" "y,m")))
+   (clobber (match_scratch:HI 3 "=2,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[2]' does not refer to the stack (which 
+     is true for 68hc11 only, we save temporary the value of Y.  */
+  if (!Y_REG_P (operands[3]))
+    {
+      output_asm_insn (\"pshy\", operands);
+      output_asm_insn (\"ldy\\t%2\", operands);
+      output_asm_insn (\"bsr\\t___ashlsi3\", operands);
+      return \"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")
+       (ashift:HI (match_operand:HI 1 "register_operand" "0")
+                   (match_operand:HI 2 "register_operand" "x")))
+   (clobber (match_dup 2))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+  return \"bsr\\t___lshlhi3\";
+}")
+
+(define_insn ""
+  [(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,!um,!*q,!*A")
+       (ashift:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0")
+                  (const_int 1)))]
+  ""
+  "@
+   aslb
+   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#1\", operands);
+      output_asm_insn (\"bcc\\t%l0\", ops);
+      output_asm_insn (\"oraa\\t#0xFE\", 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))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+  if (D_REG_P (operands[2]))
+    output_asm_insn (\"xgd%0\", operands);
+
+  output_asm_insn (\"bsr\\t___ashrhi3\", operands);
+  if (D_REG_P (operands[2]))
+    output_asm_insn (\"xgd%0\", 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 2 "const_int_operand" "")))
+   (clobber (match_scratch:HI 3 "=y"))]
+   ""
+   "*
+{
+  CC_STATUS_INIT;
+  return \"ldy\\t%2\\n\\tbsr\\t___ashrsi3\";
+}")
+
+(define_insn "*ashrsi3"
+  [(set (match_operand:SI 0 "register_operand" "+D,D")
+       (ashiftrt:SI (match_dup 0)
+                    (match_operand:HI 2 "general_operand" "y,m")))
+   (clobber (match_scratch:HI 3 "=2,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[2]' does not refer to the stack (which 
+     is true for 68hc11 only, we save temporary the value of Y.  */
+  if (!Y_REG_P (operands[3]))
+    {
+      output_asm_insn (\"pshy\", operands);
+      output_asm_insn (\"ldy\\t%2\", operands);
+      output_asm_insn (\"bsr\\t___ashrsi3\", operands);
+      return \"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,!um,!*q,!*A")
+       (ashiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0")
+                    (const_int 1)))]
+  ""
+  "@
+   asrb
+   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" "=<,um")
+       (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
+                    (const_int 32)))
+   (clobber (match_scratch:HI 2 "=A,d"))]
+   ""
+   "#")
+
+(define_split
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=<,um")
+       (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi,umi")
+                    (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" "=um")
+       (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi")
+                    (match_operand:DI 2 "const_int_operand" "")))
+   (clobber (match_scratch:HI 3 "=d"))]
+   "INTVAL (operands[2]) >= 48"
+   "#")
+
+(define_split
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=um")
+       (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi")
+                    (match_operand:DI 2 "const_int_operand" "")))
+   (clobber (match_scratch:HI 3 "=d"))]
+   "z_replacement_completed && INTVAL (operands[2]) >= 56"
+   [(set (reg:QI 1) (match_dup 9))
+    (set (reg:QI 1) (lshiftrt:QI (reg:QI 1) (match_dup 8)))
+    (set (reg:HI 1) (zero_extend:HI (reg:QI 1)))
+    (set (match_dup 4) (reg:HI 1))
+    (set (reg:QI 1) (const_int 0))
+    (set (match_dup 5) (reg:HI 1))
+    (set (match_dup 6) (reg:HI 1))
+    (set (match_dup 7) (reg:HI 1))]
+   "operands[8] = gen_rtx (CONST_INT, VOIDmode, 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" "=um")
+       (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi")
+                    (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 1) (match_dup 9))
+    (set (reg:HI 1) (lshiftrt:HI (reg:HI 1) (match_dup 8)))
+    (set (match_dup 4) (reg:HI 1))
+    (set (reg:HI 1) (const_int 0))
+    (set (match_dup 5) (reg:HI 1))
+    (set (match_dup 6) (reg:HI 1))
+    (set (match_dup 7) (reg:HI 1))]
+   "operands[8] = gen_rtx (CONST_INT, VOIDmode, 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" "=um")
+       (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi")
+                    (const_int 1)))
+   (clobber (match_scratch:HI 2 "=d"))]
+   ""
+   "#")
+
+(define_split
+  [(set (match_operand:DI 0 "non_push_operand" "=um")
+       (lshiftrt:DI (match_operand:DI 1 "general_operand" "umi")
+                    (const_int 1)))
+   (clobber (match_scratch:HI 2 "=d"))]
+   "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))
+    (set (match_dup 2) (rotatert:HI (match_dup 2) (reg:HI 7)))
+    (set (match_dup 6) (match_dup 2))
+
+    (set (match_dup 2) (match_dup 7))
+    (set (match_dup 2) (rotatert:HI (match_dup 2) (reg:HI 7)))
+    (set (match_dup 8) (match_dup 2))
+
+    (set (match_dup 2) (match_dup 9))
+    (set (match_dup 2) (rotatert:HI (match_dup 2) (reg:HI 7)))
+    (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" "=D,um")
+       (lshiftrt:SI (match_operand:SI 1 "general_operand" "uim,D")
+                    (const_int 16)))
+   (clobber (match_scratch:HI 3 "=X,X"))]
+   "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,um")
+       (lshiftrt:SI (match_operand:SI 1 "general_operand" "uim,0,D")
+                    (const_int 16)))
+   (clobber (match_scratch:HI 3 "=X,X,X"))]
+   ""
+   "#
+    xgdx\\n\\tldx\\t#0
+    #")
+
+(define_insn "*lshrsi3_const1"
+  [(set (match_operand:SI 0 "non_push_operand" "=D,*um")
+       (lshiftrt:SI (match_operand:SI 1 "nonimmediate_operand" "D*um,*um")
+                    (const_int 1)))
+   (clobber (match_scratch:HI 3 "=X,&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 2 "const_int_operand" "")))
+   (clobber (match_scratch:HI 3 "=y"))]
+   ""
+   "*
+{
+  CC_STATUS_INIT;
+  return \"ldy\\t%2\\n\\tbsr\\t___lshrsi3\";
+}")
+
+(define_insn "*lshrsi3"
+  [(set (match_operand:SI 0 "register_operand" "+D,D")
+       (lshiftrt:SI (match_dup 0)
+                    (match_operand:HI 2 "general_operand" "y,m")))
+   (clobber (match_scratch:HI 3 "=2,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[2]' does not refer to the stack (which 
+     is true for 68hc11 only, we save temporary the value of Y.  */
+  if (!Y_REG_P (operands[3]))
+    {
+      output_asm_insn (\"pshy\", operands);
+      output_asm_insn (\"ldy\\t%2\", operands);
+      output_asm_insn (\"bsr\\t___lshrsi3\", operands);
+      return \"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\\trol\\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))]
+  ""
+  "*
+{
+  CC_STATUS_INIT;
+  if (D_REG_P (operands[2]))
+    output_asm_insn (\"xgd%0\", operands);
+
+  output_asm_insn (\"bsr\\t___lshrhi3\", operands);
+  if (D_REG_P (operands[2]))
+    output_asm_insn (\"xgd%0\", operands);
+
+  return \"\"; 
+}")
+
+(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" "=d,!um,!*q,!*A")
+       (lshiftrt:QI (match_operand:QI 1 "nonimmediate_operand" "0,0,0,0")
+                    (const_int 1)))]
+  ""
+  "@
+   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 7)))]
+  ""
+  "*
+{
+  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")
+                  (reg:HI 7)))]
+  ""
+  "*
+{
+  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")
+                    (reg:HI 7)))]
+  ""
+  "*
+{
+  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 "rotlhi3"
+  [(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 "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_insn "rotrhi3"
+  [(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 \"\";
+}")
+
+;;--------------------------------------------------------------------
+;;-  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;
+}")
+
+(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" "mAi")
+        (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 (SYMBOL_REF_FLAG (XEXP (operands[0], 0)) == 1)
+        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 "general_operand" "mAi")
+             (match_operand:SI 2 "general_operand" "g")))]
+  ""
+ "*
+{
+  if (GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
+    {
+      if (SYMBOL_REF_FLAG (XEXP (operands[1], 0)) == 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_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_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\";
+  return \"rts\";
+}")
+
+(define_insn "*return_16bit"
+  [(return)
+   (use (reg:HI 1))]
+  "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\";
+  return \"rts\";
+}")
+
+(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\";
+  return \"rts\";
+}")
+
+(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
+;;--------------------------------------------------------------------
+
+;;
+;; 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 1) (match_dup 0))
+             (set (match_dup 0) (reg:HI 1))])
+   (set (reg:HI 1)
+       (plus (reg:HI 1)
+             (match_operand:HI 2 "general_operand" "")))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+             (set (match_dup 0) (reg:HI 1))])]
+  "(INTVAL (operands[1]) & 0x0FF) == 0"
+  "*
+{
+  int value_loaded = 1;
+
+  if (X_REG_P (operands[0]))
+    {
+      output_asm_insn (\"ldx\\t%2\\n\\txgdx\", 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 1))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])]
+  "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 1))
+   (set (match_dup 0) (match_dup 0))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])]
+  "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 1))
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])]
+  ""
+  "*
+{
+  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 1) (match_operand:HI 0 "hard_reg_operand" "A"))
+              (set (match_dup 0) (reg:HI 1))])
+   (set (reg:HI 1) (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 1) (match_operand:HI 0 "hard_reg_operand" "A"))
+              (set (match_dup 0) (reg:HI 1))])
+   (set (reg:QI 1) (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 1) (match_operand:HI 0 "hard_reg_operand" "A"))
+              (set (match_dup 0) (reg:HI 1))])
+   (set (reg:HI 1) (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 1) (match_operand:HI 0 "hard_reg_operand" "A"))
+              (set (match_dup 0) (reg:HI 1))])
+   (set (reg:QI 1) (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 1) (match_operand:HI 0 "hard_reg_operand" "A"))
+              (set (match_dup 0) (reg:HI 1))])
+   (parallel [(set (reg:HI 1) (match_dup 0))
+              (set (match_dup 0) (reg:HI 1))])]
+  ""
+  "*
+{
+  cc_status = cc_prev_status;
+  return \"\";
+}
+")
+
+(define_peephole
+  [(set (match_operand:HI 0 "stack_register_operand" "")
+       (plus:HI (match_dup 0) 
+                (const_int -2)))
+   (set (match_operand:HI 2 "memory_operand" "m")
+       (match_operand:HI 3 "stack_register_operand" ""))]
+  "0 && GET_CODE (operands[2]) == MEM"
+  "*
+{
+  rtx ops[2];
+
+  ops[0] = gen_rtx (MEM, HImode, 
+                   gen_rtx (PRE_DEC, HImode, stack_pointer_rtx));
+  ops[1] = operands[3];
+  m68hc11_gen_movhi (insn, ops);
+  return \"\";
+}
+")
+
+(define_peephole
+  [(set (match_operand:HI 0 "hard_reg_operand" "")
+       (match_operand:HI 1 "memory_operand" "m"))
+   (set (match_operand:HI 2 "stack_register_operand" "")
+       (plus:HI (match_dup 2) 
+                (const_int 2)))]
+  "GET_CODE (operands[1]) == MEM"
+  "*
+{
+  rtx ops[2];
+
+  ops[0] = operands[0];
+  ops[1] = gen_rtx (MEM, HImode,
+                   gen_rtx (POST_INC, HImode, stack_pointer_rtx));
+  m68hc11_gen_movhi (insn, ops);
+  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 3)))]
+  "TARGET_M6811"
+  "*
+{
+  return \"sts\\t%t0\\n\\tld%0\\t%t0\";
+}
+")
diff --git a/gcc/config/m68hc11/m68hc12.h b/gcc/config/m68hc11/m68hc12.h
new file mode 100644 (file)
index 0000000..c911c47
--- /dev/null
@@ -0,0 +1,43 @@
+/* Definitions of target machine for GNU compiler, for m68hc12.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Contributed by Stephane Carrez (stcarrez@worldnet.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.  */
+
+/* Compile and assemble for a 68hc12 unless there is a -m68hc11 option.  */
+#define ASM_SPEC       "%{m68hc11:-m68hc11}%{!m68hc11:-m68hc12}"
+#define LIB_SPEC       ""
+#define CC1_SPEC       ""
+
+/* We need to tell the linker the target elf format.  Just pass an
+   emulation option.  This can be overriden by -Wl option of gcc.  */
+#define LINK_SPEC      "%{m68hc11:-m m68hc11elf}%{!m68hc11:-m m68hc12elf}"
+
+#define CPP_SPEC  \
+"%{mshort:-D__HAVE_SHORT_INT__ -D__INT__=16 -D__INT_MAX__=32767}\
+ %{!mshort:-D__INT__=32 -D__INT_MAX__=2147483647}\
+ %{m68hc11:-Dmc6811 -DMC6811 -Dmc68hc11}\
+ %{!m68hc11:-Dmc6812 -DMC6812 -Dmc68hc12}"
+
+/* Default target_flags if no switches specified.  */
+#define TARGET_DEFAULT         (MASK_M6812)
+
+#define TARGET_M68HC12
+
+#include "m68hc11/m68hc11.h"
+
diff --git a/gcc/config/m68hc11/t-m68hc11-gas b/gcc/config/m68hc11/t-m68hc11-gas
new file mode 100644 (file)
index 0000000..221a853
--- /dev/null
@@ -0,0 +1,79 @@
+RANLIB_FOR_TARGET = ` \
+  if [ -f $(objdir)/../binutils/ranlib ] ; then \
+    echo $(objdir)/../binutils/ranlib ; \
+  else \
+    if [ "$(host_canonical)" = "$(target)" ] ; then \
+      echo ranlib; \
+    else \
+       if [ -f $(bindir)/$(target_alias)-ranlib ] ; then \
+         echo $(bindir)/$(target_alias)-ranlib ; \
+       else \
+          t='$(program_transform_cross_name)'; echo ranlib | sed -e $$t ; \
+       fi; \
+    fi; \
+  fi`
+
+T_CPPFLAGS = -DUSE_GAS
+
+CROSS_LIBGCC1 = libgcc1-asm.a
+LIB1ASMSRC = m68hc11/larith.asm
+LIB1ASMFUNCS = _mulsi3 \
+       _mulqi3 _ashlsi3 _ashrsi3 _lshrsi3 \
+       _divmodhi4 _mulhi3 _mulhi32 \
+       _memcpy _memset _negsi2 _one_cmplsi2 \
+       _regs_min _regs_d1_8 _regs_d8_16 _regs_d17_32 \
+       _premain __exit _abort _cleanup \
+       _adddi3 _subdi3 _notdi2 \
+       _ashrhi3 _lshrhi3 _lshlhi3 _ashrqi3 _lshlqi3 _map_data _init_bss
+
+TARGET_LIBGCC2_CFLAGS = -DUSE_GAS -DIN_GCC
+
+# 32-bit div/mod from the mn10200 port.  Prototypes have been added
+# to avoid problems in passing 16/32-bit int (last param of udivmodsi4).
+LIB2FUNCS_EXTRA = $(srcdir)/config/m68hc11/udivmodsi4.c \
+       $(srcdir)/config/m68hc11/divmod.c $(srcdir)/config/m68hc11/udivmod.c
+
+# Don't compile with -g1 this reduces the size of some sections (.eh_frame).
+LIBGCC2_DEBUG_CFLAGS =
+LIBGCC2_CFLAGS = -Os $(LIBGCC2_INCLUDES) $(TARGET_LIBGCC2_CFLAGS) $(LIBGCC2_DEBUG_CFLAGS) $(GTHREAD_FLAGS) -DIN_LIBGCC2
+
+MULTILIB_OPTIONS  = m68hc11/m68hc12 mshort fshort-double
+MULTILIB_DIRNAMES =
+MULTILIB_MATCHES  = m68hc11=m6811 m68hc12=m6812
+MULTILIB_EXCEPTIONS = -mnoshort -mno68hc11
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
+
+# We want fine grained libraries, so use the new code to build the
+# floating point emulation libraries.
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+       echo '#define SMALL_MACHINE' >> dp-bit.c
+       echo '#define CMPtype HItype' >> dp-bit.c
+       echo '#ifdef __LITTLE_ENDIAN__' > dp-bit.c
+       echo '#define FLOAT_BIT_ORDER_MISMATCH' >>dp-bit.c
+       echo '#endif'           >> dp-bit.c
+       cat $(srcdir)/config/fp-bit.c >> dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+       echo '#define FLOAT' > fp-bit.c
+       echo '#define CMPtype HItype' >> fp-bit.c
+       echo '#define SMALL_MACHINE' >> fp-bit.c
+       echo '#ifdef __LITTLE_ENDIAN__' >> fp-bit.c
+       echo '#define FLOAT_BIT_ORDER_MISMATCH' >>fp-bit.c
+       echo '#endif'           >> fp-bit.c
+       cat $(srcdir)/config/fp-bit.c >> fp-bit.c
+
+CRT0_S = $(srcdir)/config/m68hc11/m68hc11-crt0.S
+MCRT0_S= $(srcdir)/config/m68hc11/m68hc11-crt0.S
+
+CRT0STUFF_T_CFLAGS =
+
+# Assemble startup files.
+$(T)crt1.o: $(CRT0_S) $(GCC_PASSES)
+       $(GCC_FOR_TARGET) $(MULTILIB_CFLAGS) -c -o $(T)crt1.o -x assembler-with-cpp $(CRT0_S)
+
+EXTRA_MULTILIB_PARTS = crt1.o
diff --git a/gcc/config/m68hc11/xm-m68hc11.h b/gcc/config/m68hc11/xm-m68hc11.h
new file mode 100644 (file)
index 0000000..bd471f0
--- /dev/null
@@ -0,0 +1,24 @@
+/* Configuration for GNU C-compiler for Motorola 68HC11 and 68HC12.
+   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Contributed by Stephane Carrez (stcarrez@worldnet.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.  */
+
+#include "tm.h"
+
+#define inhibit_libc