;;- Machine description for the pdp11 for GNU C compiler
;; Copyright (C) 1994, 1995 Free Software Foundation, Inc.
;; Contributed by Michael K. Gschwind (mike@vlsivie.tuwien.ac.at).

;; This file is part of GNU CC.

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

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

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


;; HI is 16 bit
;; QI is 8 bit 

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

;;- Operand classes for the register allocator:

;; Compare instructions.

;; currently we only support df floats, which saves us quite some
;; hassle switching the FP mode! 
;; we assume that CPU is always in long float mode, and 
;; 16 bit integer mode - currently, the prologue for main does this,
;; but maybe we should just set up a NEW crt0 properly, 
;; -- and what about signal handling code?
;; (we don't even let sf floats in the register file, so
;; we only should have to worry about truncating and widening 
;; when going to memory)

;; abort() call by g++ - must define libfunc for cmp_optab
;; and ucmp_optab for mode SImode, because we don't have that!!!
;; - yet since no libfunc is there, we abort ()

;; The only thing that remains to be done then is output 
;; the floats in a way the assembler can handle it (and 
;; if you're really into it, use a PDP11 float emulation
;; libary to do floating point constant folding - but 
;; I guess you'll get reasonable results even when not
;; doing this)
;; the last thing to do is fix the UPDATE_CC macro to check
;; for floating point condition codes, and set cc_status
;; properly, also setting the CC_IN_FCCR flag. 

;; define attributes
;; currently type is only fpu or arith or unknown, maybe branch later ?
;; default is arith
(define_attr "type" "unknown,arith,fp" (const_string "arith"))

;; length default is 1 word each
(define_attr "length" "" (const_int 1))

;; a user's asm statement
(define_asm_attributes
  [(set_attr "type" "unknown")
; all bets are off how long it is - make it 256, forces long jumps 
; whenever jumping around it !!!
   (set_attr "length" "256")])

;; define function units

;; arithmetic - values here immediately when next insn issued
;; or does it mean the number of cycles after this insn was issued?
;; how do I say that fpu insns use cpu also? (pre-interaction phase)

;(define_function_unit "cpu" 1 1 (eq_attr "type" "arith") 0 0)
;(define_function_unit "fpu" 1 1 (eq_attr "type" "fp") 0 0)

;; compare
(define_insn "cmpdf"
  [(set (cc0)
	(compare (match_operand:DF 0 "general_operand" "fR,Q,F")
		 (match_operand:DF 1 "register_operand" "a,a,a")))]
  "TARGET_FPU"
  "*
{
  cc_status.flags = CC_IN_FPU;
  return \"cmpd %0, %1\;cfcc\";
}"
  [(set_attr "length" "2,3,6")])

;; a bit of brain damage, maybe inline later - 
;; problem is - gcc seems to NEED SImode because 
;; of the cmp weirdness - maybe change gcc to handle this?

(define_expand "cmpsi"
  [(set (reg:SI 0)
	(match_operand:SI 0 "general_operand" "g"))
   (set (reg:SI 2)
	(match_operand:SI 1 "general_operand" "g"))
   (parallel [(set (cc0)
		   (compare (reg:SI 0)
			    (reg:SI 2)))
	      (clobber (reg:SI 0))])]
  "0" ;; disable for test
  "")

;; check for next insn for branch code - does this still
;; work in gcc 2.* ?

(define_insn ""
  [(set (cc0)
	(compare (reg:SI 0)
		 (reg:SI 2)))
   (clobber (reg:SI 0))]
  ""
  "*
{
  rtx br_insn = NEXT_INSN (insn);
  RTX_CODE br_code;

  if (GET_CODE (br_insn) != JUMP_INSN)
    abort();
  br_code =  GET_CODE (XEXP (XEXP (PATTERN (br_insn), 1), 0));
  
  switch(br_code)
  {
    case GEU:
    case LTU:
    case GTU:
    case LEU:
      
      return \"jsr pc, ___ucmpsi\;cmp $1,r0\";

    case GE:
    case LT:
    case GT:
    case LE:
    case EQ:
    case NE:

      return \"jsr pc, ___cmpsi\;tst r0\";

    default:

      abort();
  }
}"
  [(set_attr "length" "4")])


(define_insn "cmphi"
  [(set (cc0)
	(compare (match_operand:HI 0 "general_operand" "rR,rR,Qi,Qi")
		 (match_operand:HI 1 "general_operand" "rR,Qi,rR,Qi")))]
  ""
  "cmp %0,%1"
  [(set_attr "length" "1,2,2,3")])

(define_insn "cmpqi"
  [(set (cc0)
	(compare (match_operand:QI 0 "general_operand" "rR,rR,Qi,Qi")
		 (match_operand:QI 1 "general_operand" "rR,Qi,rR,Qi")))]
  ""
  "cmpb %0,%1"
  [(set_attr "length" "1,2,2,3")])
			   

;; We have to have this because cse can optimize the previous pattern
;; into this one.

(define_insn "tstdf"
  [(set (cc0)
	(match_operand:DF 0 "general_operand" "fR,Q"))]
  "TARGET_FPU"
  "*
{
  cc_status.flags = CC_IN_FPU;
  return \"tstd %0\;cfcc\";
}"
  [(set_attr "length" "2,3")])


(define_expand "tstsi"
  [(set (reg:SI 0)
	(match_operand:SI 0 "general_operand" "g"))
   (parallel [(set (cc0)
		   (reg:SI 0))
	      (clobber (reg:SI 0))])]
  "0" ;; disable for test
  "")

(define_insn ""
  [(set (cc0)
	(reg:SI 0))
   (clobber (reg:SI 0))]
  ""
  "jsr pc, ___tstsi\;tst r0"
  [(set_attr "length" "3")])


(define_insn "tsthi"
  [(set (cc0)
	(match_operand:HI 0 "general_operand" "rR,Q"))]
  ""
  "tst %0"
  [(set_attr "length" "1,2")])

(define_insn "tstqi"
  [(set (cc0)
	(match_operand:QI 0 "general_operand" "rR,Q"))]
  ""
  "tstb %0"
  [(set_attr "length" "1,2")])

;; sob instruction - we need an assembler which can make this instruction
;; valid under _all_ circumstances!

(define_insn ""
  [(set (pc)
	(if_then_else
	 (ne (plus:HI (match_operand:HI 0 "register_operand" "r")
		      (const_int -1))
	     (const_int 0))
	 (label_ref (match_operand 1 "" ""))
	 (pc)))
   (set (match_dup 0)
	(plus:HI (match_dup 0)
		 (const_int -1)))]
  "TARGET_40_PLUS"
  "*
{
 static int labelcount = 0;
 static char buf[1000];

 if (get_attr_length (insn) == 1)
    return \"sob %0, %l1\";

 /* emulate sob */
 output_asm_insn (\"dec %0\", operands);
 
 sprintf (buf, \"bge LONG_SOB%d\", labelcount);
 output_asm_insn (buf, NULL);

 output_asm_insn (\"jmp %l1\", operands);
 
 sprintf (buf, \"LONG_SOB%d:\", labelcount++);
 output_asm_insn (buf, NULL);

 return \"\";
}"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						       (pc))
						(const_int -256))
					   (ge (minus (match_dup 0)
						       (pc))
						(const_int 0)))
				      (const_int 4)
				      (const_int 1)))])

;; These control RTL generation for conditional jump insns
;; and match them for register allocation.

;; problem with too short jump distance! we need an assembler which can 
;; make this valid for all jump distances!
;; e.g. gas!

;; these must be changed to check for CC_IN_FCCR if float is to be 
;; enabled

(define_insn "beq"
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"beq\", \"bne\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])


(define_insn "bne"
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"bne\", \"beq\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn "bgt"
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"bgt\", \"ble\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn "bgtu"
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"bhi\", \"blos\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn "blt"
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"blt\", \"bge\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])


(define_insn "bltu"
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"blo\", \"bhos\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn "bge"
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"bge\", \"blt\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn "bgeu"
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"bhis\", \"blo\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn "ble"
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"ble\", \"bgt\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn "bleu"
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (label_ref (match_operand 0 "" ""))
		      (pc)))]
  ""
  "* return output_jump(\"blos\", \"bhi\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])


;; These match inverted jump insns for register allocation.

(define_insn ""
  [(set (pc)
	(if_then_else (eq (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"bne\", \"beq\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (ne (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"beq\", \"bne\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (gt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"ble\", \"bgt\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (gtu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"blos\", \"bhi\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (lt (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"bge\", \"blt\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (ltu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"bhos\", \"blo\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (ge (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""  
  "* return output_jump(\"blt\", \"bge\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (geu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"blo\", \"bhos\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (le (cc0)
			  (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"bgt\", \"ble\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

(define_insn ""
  [(set (pc)
	(if_then_else (leu (cc0)
			   (const_int 0))
		      (pc)
		      (label_ref (match_operand 0 "" ""))))]
  ""
  "* return output_jump(\"bhi\", \"blos\", get_attr_length(insn));"
  [(set (attr "length") (if_then_else (ior (le (minus (match_dup 0)
						      (pc))
					       (const_int -128))
					   (ge (minus (match_dup 0)
						      (pc))
					       (const_int 128)))
				      (const_int 3)
				      (const_int 1)))])

;; Move instructions

(define_insn "movdi"
  [(set (match_operand:DI 0 "general_operand" "=g")
	(match_operand:DI 1 "general_operand" "g"))]
  ""
  "* return output_move_quad (operands);"
;; what's the mose expensive code - say twice movsi = 16
  [(set_attr "length" "16")])

(define_insn "movsi"
  [(set (match_operand:SI 0 "general_operand" "=r,r,r,rm,m")
	(match_operand:SI 1 "general_operand" "rN,IJ,K,m,r"))]
  ""
  "* return output_move_double (operands);"
;; what's the most expensive code ? - I think 8!
;; we could split it up and make several sub-cases...
  [(set_attr "length" "2,3,4,8,8")])

(define_insn "movhi"
  [(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
	(match_operand:HI 1 "general_operand" "rRN,Qi,rRN,Qi"))]
  ""
  "*
{
  if (operands[1] == const0_rtx)
    return \"clr %0\";

  return \"mov %1, %0\";
}"
  [(set_attr "length" "1,2,2,3")])

(define_insn "movqi"
  [(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
	(match_operand:QI 1 "general_operand" "rRN,Qi,rRN,Qi"))]
  ""
  "*
{
  if (operands[1] == const0_rtx)
    return \"clrb %0\";

  return \"movb %1, %0\";
}"
  [(set_attr "length" "1,2,2,3")])

;; do we have to supply all these moves? e.g. to 
;; NO_LOAD_FPU_REGs ? 
(define_insn "movdf"
  [(set (match_operand:DF 0 "general_operand" "=f,R,f,Q,f,m")
        (match_operand:DF 1 "general_operand" "fR,f,Q,f,F,m"))]
  ""
  "* return output_move_quad (operands);"
;; just a guess..
  [(set_attr "length" "1,1,2,2,5,16")])

(define_insn "movsf"
  [(set (match_operand:SF 0 "general_operand" "=g,r,g")
        (match_operand:SF 1 "general_operand" "r,rmF,g"))]
  "TARGET_FPU"
  "* return output_move_double (operands);"
  [(set_attr "length" "8,8,8")])

;; maybe fiddle a bit with move_ratio, then 
;; let constraints only accept a register ...

(define_expand "movstrhi"
  [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" "=g,g"))
		   (mem:BLK (match_operand:BLK 1 "general_operand" "g,g")))
	      (use (match_operand:HI 2 "arith_operand" "n,&mr"))
	      (use (match_operand:HI 3 "immediate_operand" "i,i"))
	      (clobber (match_scratch:HI 4 "=&r,X"))
	      (clobber (match_dup 0))
	      (clobber (match_dup 1))
	      (clobber (match_dup 2))])]
  "(TARGET_BCOPY_BUILTIN)"
  "
{
  operands[0] = copy_to_mode_reg (Pmode, XEXP (operands[0], 0));
  operands[1] = copy_to_mode_reg (Pmode, XEXP (operands[1], 0));
  operands[2] = force_not_mem (operands[2]);
}")


(define_insn "" ; "movstrhi"
  [(set (mem:BLK (match_operand:HI 0 "general_operand" "=r,r"))
	(mem:BLK (match_operand:HI 1 "general_operand" "r,r")))
   (use (match_operand:HI 2 "arith_operand" "n,&r"))
   (use (match_operand:HI 3 "immediate_operand" "i,i"))
   (clobber (match_scratch:HI 4 "=&r,X"))
   (clobber (match_dup 0))
   (clobber (match_dup 1))
   (clobber (match_dup 2))]
  "(TARGET_BCOPY_BUILTIN)"
  "* return output_block_move (operands);"
;;; just a guess
  [(set_attr "length" "40")])
   


;;- truncation instructions

(define_insn  "truncdfsf2"
  [(set (match_operand:SF 0 "memory_operand" "=R,Q")
	(float_truncate:SF (match_operand:DF 1 "register_operand" "a,a")))]
  "TARGET_FPU"
  "stcdf %1, %0"
  [(set_attr "length" "1,2")])

(define_expand "truncsihi2"
  [(set (match_operand:HI 0 "general_operand" "=g")
	(subreg:HI 
	  (match_operand:SI 1 "general_operand" "or")
          0))]
  ""
  "")


;;- zero extension instructions

(define_insn "zero_extendqihi2"
  [(set (match_operand:HI 0 "general_operand" "=r")
	(zero_extend:HI (match_operand:QI 1 "general_operand" "0")))]
  ""
  "bic $(256*255), %0"
  [(set_attr "length" "2")])
			 
(define_expand "zero_extendhisi2"
  [(set (subreg:HI 
          (match_dup 0)
          1)
        (match_operand:HI 1 "register_operand" "r"))
   (set (subreg:HI 
          (match_operand:SI 0 "register_operand" "=r")
          0)
        (const_int 0))]
  ""
  "/* operands[1] = make_safe_from (operands[1], operands[0]); */")


;;- sign extension instructions

(define_insn "extendsfdf2"
  [(set (match_operand:DF 0 "register_operand" "=a,a")
	(float_extend:SF (match_operand:SF 1 "memory_operand" "R,Q")))]
  "TARGET_FPU"
  "ldcfd %1, %0"
  [(set_attr "length" "1,2")])

;; does movb sign extend in register-to-register move?
(define_insn "extendqihi2"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
	(sign_extend:HI (match_operand:QI 1 "general_operand" "rR,Q")))]
  ""
  "movb %1, %0"
  [(set_attr "length" "1,2")])

(define_insn "extendqisi2"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(sign_extend:SI (match_operand:QI 1 "general_operand" "rR,Q")))]
  "TARGET_40_PLUS"
  "*
{
  rtx latehalf[2];

  /* make register pair available */
  latehalf[0] = operands[0];
  operands[0] = gen_rtx(REG, HImode, REGNO (operands[0])+1);
    
  output_asm_insn(\"movb %1, %0\", operands);
  output_asm_insn(\"sxt %0\", latehalf);
    
  return \"\";
}"
  [(set_attr "length" "2,3")])

;; maybe we have to use define_expand to say that we have the instruction,
;; unconditionally, and then match dependent on CPU type:

(define_expand "extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "=g")
	(sign_extend:SI (match_operand:HI 1 "general_operand" "g")))]
  ""
  "")
  
(define_insn "" ; "extendhisi2"
  [(set (match_operand:SI 0 "general_operand" "=o,<,r")
	(sign_extend:SI (match_operand:HI 1 "general_operand" "g,g,g")))]
  "TARGET_40_PLUS"
  "*
{
  rtx latehalf[2];

  /* we don't want to mess with auto increment */
  
  switch(which_alternative)
  {
    case 0:

      latehalf[0] = operands[0];
      operands[0] = adj_offsettable_operand(operands[0], 2);
  
      output_asm_insn(\"mov %1, %0\", operands);
      output_asm_insn(\"sxt %0\", latehalf);

      return \"\";

    case 1:

      /* - auto-decrement - right direction ;-) */
      output_asm_insn(\"mov %1, %0\", operands);
      output_asm_insn(\"sxt %0\", operands);

      return \"\";

    case 2:

      /* make register pair available */
      latehalf[0] = operands[0];
      operands[0] = gen_rtx(REG, HImode, REGNO (operands[0])+1);

      output_asm_insn(\"mov %1, %0\", operands);
      output_asm_insn(\"sxt %0\", latehalf);

      return \"\";

    default:

      abort();
  }
}"
  [(set_attr "length" "5,3,3")])


(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r")
	(sign_extend:SI (match_operand:HI 1 "general_operand" "0")))]
  "(! TARGET_40_PLUS)"
  "*
{
  static count = 0;
  char buf[100];
  rtx lateoperands[2];

  lateoperands[0] = operands[0];
  operands[0] = gen_rtx(REG, HImode, REGNO (operands[0])+1);

  output_asm_insn(\"tst %0\", operands);
  sprintf(buf, \"bge extendhisi%d\", count);
  output_asm_insn(buf, NULL);
  output_asm_insn(\"mov -1, %0\", lateoperands);
  sprintf(buf, \"bne extendhisi%d\", count+1);
  output_asm_insn(buf, NULL);
  sprintf(buf, \"\\nextendhisi%d:\", count);
  output_asm_insn(buf, NULL);
  output_asm_insn(\"clr %0\", lateoperands);
  sprintf(buf, \"\\nextendhisi%d:\", count+1);
  output_asm_insn(buf, NULL);

  count += 2;

  return \"\";
}"
  [(set_attr "length" "6")])

;; make float to int and vice versa 
;; using the cc_status.flag field we could probably cut down
;; on seti and setl
;; assume that we are normally in double and integer mode -
;; what do pdp library routines do to fpu mode ?

(define_insn "floatsidf2"
  [(set (match_operand:DF 0 "register_operand" "=a,a")
	(float:DF (match_operand:SI 1 "memory_operand" "R,Q")))]
  "TARGET_FPU"
  "setl\;ldcld %1, %0\;seti"
  [(set_attr "length" "3,4")])

(define_insn "floathidf2"
  [(set (match_operand:DF 0 "register_operand" "=a,a")
	(float:DF (match_operand:HI 1 "general_operand" "rR,Qi")))]
  "TARGET_FPU"
  "ldcid %1, %0"
  [(set_attr "length" "1,2")])
	
;; cut float to int
(define_insn "fix_truncdfsi2"
  [(set (match_operand:SI 0 "memory_operand" "=R,Q")
	(fix:SI (fix:DF (match_operand:DF 1 "register_operand" "a,a"))))]
  "TARGET_FPU"
  "setl\;stcdl %1, %0\;seti"
  [(set_attr "length" "3,4")])

(define_insn "fix_truncdfhi2"
  [(set (match_operand:HI 0 "general_operand" "=rR,Q")
	(fix:HI (fix:DF (match_operand:DF 1 "register_operand" "a,a"))))]
  "TARGET_FPU"
  "stcdi %1, %0"
  [(set_attr "length" "1,2")])


;;- arithmetic instructions
;;- add instructions

(define_insn "adddf3"
  [(set (match_operand:DF 0 "register_operand" "=a,a,a")
	(plus:DF (match_operand:DF 1 "register_operand" "%0,0,0")
		 (match_operand:DF 2 "general_operand" "fR,Q,F")))]
  "TARGET_FPU"
  "addd %2, %0"
  [(set_attr "length" "1,2,5")])

(define_insn "addsi3"
  [(set (match_operand:SI 0 "general_operand" "=r,r,o,o,r,r,r,o,o,o")
	(plus:SI (match_operand:SI 1 "general_operand" "%0,0,0,0,0,0,0,0,0,0")
		 (match_operand:SI 2 "general_operand" "r,o,r,o,I,J,K,I,J,K")))]
  ""
  "*
{ /* Here we trust that operands don't overlap 

     or is lateoperands the low word?? - looks like it! */

  unsigned int i;
  rtx lateoperands[3];
  
  lateoperands[0] = operands[0];

  if (REG_P (operands[0]))
    operands[0] = gen_rtx(REG, HImode, REGNO(operands[0]) + 1);
  else
    operands[0] = adj_offsettable_operand (operands[0], 2);
  
  if (! CONSTANT_P(operands[2]))
  {
    lateoperands[2] = operands[2];

    if (REG_P (operands[2]))
      operands[2] = gen_rtx(REG, HImode, REGNO(operands[2]) + 1);
    else
      operands[2] = adj_offsettable_operand(operands[2], 2);

    output_asm_insn (\"add %2, %0\", operands);
    output_asm_insn (\"adc %0\", lateoperands);
    output_asm_insn (\"add %2, %0\", lateoperands);
    return \"\";
  }

  lateoperands[2] = gen_rtx(CONST_INT, VOIDmode, (INTVAL(operands[2]) >> 16) & 0xffff);
  operands[2] = gen_rtx(CONST_INT, VOIDmode, INTVAL(operands[2]) & 0xffff);
  
  if (INTVAL(operands[2]))
  { 
    output_asm_insn (\"add %2, %0\", operands);
    output_asm_insn (\"adc %0\", lateoperands);
  }

  if (INTVAL(lateoperands[2]))
    output_asm_insn (\"add %2, %0\", lateoperands);

  return \"\";
}"
  [(set_attr "length" "3,5,6,8,3,1,5,5,3,8")])

(define_insn "addhi3"
  [(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
	(plus:HI (match_operand:HI 1 "general_operand" "%0,0,0,0")
		 (match_operand:HI 2 "general_operand" "rRLM,Qi,rRLM,Qi")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    if (INTVAL(operands[2]) == 1)
      return \"inc %0\";
    else if (INTVAL(operands[2]) == -1)
      return \"dec %0\";

  return \"add %2, %0\";
}"
  [(set_attr "length" "1,2,2,3")])

(define_insn "addqi3"
  [(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
	(plus:QI (match_operand:QI 1 "general_operand" "%0,0,0,0")
		 (match_operand:QI 2 "general_operand" "rRLM,Qi,rRLM,Qi")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    if (INTVAL(operands[2]) == 1)
      return \"incb %0\";
    else if (INTVAL(operands[2]) == -1)
      return \"decb %0\";

  return \"addb %2, %0\";
}"
  [(set_attr "length" "1,2,2,3")])


;;- subtract instructions
;; we don't have to care for constant second 
;; args, since they are canonical plus:xx now!
;; also for minus:DF ??

(define_insn "subdf3"
  [(set (match_operand:DF 0 "register_operand" "=a,a")
	(minus:DF (match_operand:DF 1 "register_operand" "0,0")
		  (match_operand:DF 2 "general_operand" "fR,Q")))]
  "TARGET_FPU"
  "subd %2, %0"
  [(set_attr "length" "1,2")])

(define_insn "subsi3"
  [(set (match_operand:SI 0 "general_operand" "=r,r,o,o")
        (minus:SI (match_operand:SI 1 "general_operand" "0,0,0,0")
                  (match_operand:SI 2 "general_operand" "r,o,r,o")))]
  ""
  "*
{ /* Here we trust that operands don't overlap 

     or is lateoperands the low word?? - looks like it! */

  unsigned int i;
  rtx lateoperands[3];
  
  lateoperands[0] = operands[0];

  if (REG_P (operands[0]))
    operands[0] = gen_rtx(REG, HImode, REGNO(operands[0]) + 1);
  else
    operands[0] = adj_offsettable_operand (operands[0], 2);
  
  lateoperands[2] = operands[2];

  if (REG_P (operands[2]))
    operands[2] = gen_rtx(REG, HImode, REGNO(operands[2]) + 1);
  else
    operands[2] = adj_offsettable_operand(operands[2], 2);

  output_asm_insn (\"sub %2, %0\", operands);
  output_asm_insn (\"sbc %0\", lateoperands);
  output_asm_insn (\"sub %2, %0\", lateoperands);
  return \"\";
}"
;; offsettable memory addresses always are expensive!!!
  [(set_attr "length" "3,5,6,8")])

(define_insn "subhi3"
  [(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
	(minus:HI (match_operand:HI 1 "general_operand" "0,0,0,0")
		  (match_operand:HI 2 "general_operand" "rR,Qi,rR,Qi")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    abort();

  return \"sub %2, %0\";
}"
  [(set_attr "length" "1,2,2,3")])

(define_insn "subqi3"
  [(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
	(minus:QI (match_operand:QI 1 "general_operand" "0,0,0,0")
		  (match_operand:QI 2 "general_operand" "rR,Qi,rR,Qi")))]
  ""
  "*
{
  if (GET_CODE (operands[2]) == CONST_INT)
    abort();

  return \"subb %2, %0\";
}"
  [(set_attr "length" "1,2,2,3")])

;;;;- and instructions
;; Bit-and on the pdp (like on the vax) is done with a clear-bits insn.
(define_expand "andsi3"
  [(set (match_operand:SI 0 "general_operand" "=g")
	(and:SI (match_operand:SI 1 "general_operand" "0")
		(not:SI (match_operand:SI 2 "general_operand" "g"))))]
  ""
  "
{
  extern rtx expand_unop ();
  if (GET_CODE (operands[2]) == CONST_INT)
    operands[2] = gen_rtx (CONST_INT, VOIDmode, ~INTVAL (operands[2]));
  else
    operands[2] = expand_unop (SImode, one_cmpl_optab, operands[2], 0, 1);
}")

(define_expand "andhi3"
  [(set (match_operand:HI 0 "general_operand" "=g")
	(and:HI (match_operand:HI 1 "general_operand" "0")
		(not:HI (match_operand:HI 2 "general_operand" "g"))))]
  ""
  "
{
  extern rtx expand_unop ();
  if (GET_CODE (operands[2]) == CONST_INT)
    operands[2] = gen_rtx (CONST_INT, VOIDmode, ~INTVAL (operands[2]));
  else
    operands[2] = expand_unop (HImode, one_cmpl_optab, operands[2], 0, 1);
}")

(define_expand "andqi3"
  [(set (match_operand:QI 0 "general_operand" "=g")
	(and:QI (match_operand:QI 1 "general_operand" "0")
		(not:QI (match_operand:QI 2 "general_operand" "g"))))]
  ""
  "
{
  extern rtx expand_unop ();
  rtx op = operands[2];
  if (GET_CODE (op) == CONST_INT)
    operands[2] = gen_rtx (CONST_INT, VOIDmode,
			   ((1 << 8) - 1) & ~INTVAL (op));
  else
    operands[2] = expand_unop (QImode, one_cmpl_optab, op, 0, 1);
}")

(define_insn "andcbsi3"
  [(set (match_operand:SI 0 "general_operand" "=r,r,o,o,r,r,r,o,o,o")
        (and:SI (match_operand:SI 1 "general_operand" "%0,0,0,0,0,0,0,0,0,0")
                (not:SI (match_operand:SI 2 "general_operand" "r,o,r,o,I,J,K,I,J,K"))))]
  ""
  "*
{ /* Here we trust that operands don't overlap 

     or is lateoperands the low word?? - looks like it! */

  unsigned int i;
  rtx lateoperands[3];
  
  lateoperands[0] = operands[0];

  if (REG_P (operands[0]))
    operands[0] = gen_rtx(REG, HImode, REGNO(operands[0]) + 1);
  else
    operands[0] = adj_offsettable_operand (operands[0], 2);
  
  if (! CONSTANT_P(operands[2]))
  {
    lateoperands[2] = operands[2];

    if (REG_P (operands[2]))
      operands[2] = gen_rtx(REG, HImode, REGNO(operands[2]) + 1);
    else
      operands[2] = adj_offsettable_operand(operands[2], 2);

    output_asm_insn (\"bic %2, %0\", operands);
    output_asm_insn (\"bic %2, %0\", lateoperands);
    return \"\";
  }

  lateoperands[2] = gen_rtx(CONST_INT, VOIDmode, (INTVAL(operands[2]) >> 16) & 0xffff);
  operands[2] = gen_rtx(CONST_INT, VOIDmode, INTVAL(operands[2]) & 0xffff);
  
  /* these have different lengths, so we should have 
     different constraints! */
  if (INTVAL(operands[2]))
    output_asm_insn (\"bic %2, %0\", operands);

  if (INTVAL(lateoperands[2]))
    output_asm_insn (\"bic %2, %0\", lateoperands);

  return \"\";
}"
  [(set_attr "length" "2,4,4,6,2,2,4,3,3,6")])

(define_insn "andcbhi3"
  [(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
	(and:HI (match_operand:HI 1 "general_operand" "0,0,0,0")
		(not:HI (match_operand:HI 2 "general_operand" "rR,Qi,rR,Qi"))))]
  ""
  "bic %2, %0"
  [(set_attr "length" "1,2,2,3")])

(define_insn "andcbqi3"
  [(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
	(and:QI (match_operand:QI 1 "general_operand" "0,0,0,0")
		(not:QI (match_operand:QI 2 "general_operand" "rR,Qi,rR,Qi"))))]
  ""
  "bicb %2, %0"
  [(set_attr "length" "1,2,2,3")])

;;- Bit set (inclusive or) instructions
(define_insn "iorsi3"
  [(set (match_operand:SI 0 "general_operand" "=r,r,o,o,r,r,r,o,o,o")
        (ior:SI (match_operand:SI 1 "general_operand" "%0,0,0,0,0,0,0,0,0,0")
                  (match_operand:SI 2 "general_operand" "r,o,r,o,I,J,K,I,J,K")))]
  ""
  "*
{ /* Here we trust that operands don't overlap 

     or is lateoperands the low word?? - looks like it! */

  unsigned int i;
  rtx lateoperands[3];
  
  lateoperands[0] = operands[0];

  if (REG_P (operands[0]))
    operands[0] = gen_rtx(REG, HImode, REGNO(operands[0]) + 1);
  else
    operands[0] = adj_offsettable_operand (operands[0], 2);
  
  if (! CONSTANT_P(operands[2]))
  {
    lateoperands[2] = operands[2];

    if (REG_P (operands[2]))
      operands[2] = gen_rtx(REG, HImode, REGNO(operands[2]) + 1);
    else
      operands[2] = adj_offsettable_operand(operands[2], 2);

    output_asm_insn (\"bis %2, %0\", operands);
    output_asm_insn (\"bis %2, %0\", lateoperands);
    return \"\";
  }

  lateoperands[2] = gen_rtx(CONST_INT, VOIDmode, (INTVAL(operands[2]) >> 16) & 0xffff);
  operands[2] = gen_rtx(CONST_INT, VOIDmode, INTVAL(operands[2]) & 0xffff);
  
  /* these have different lengths, so we should have 
     different constraints! */
  if (INTVAL(operands[2]))
    output_asm_insn (\"bis %2, %0\", operands);

  if (INTVAL(lateoperands[2]))
    output_asm_insn (\"bis %2, %0\", lateoperands);

  return \"\";
}"
  [(set_attr "length" "2,4,4,6,2,2,4,3,3,6")])

(define_insn "iorhi3"
  [(set (match_operand:HI 0 "general_operand" "=rR,rR,Q,Q")
	(ior:HI (match_operand:HI 1 "general_operand" "%0,0,0,0")
		(match_operand:HI 2 "general_operand" "rR,Qi,rR,Qi")))]
  ""
  "bis %2, %0"
  [(set_attr "length" "1,2,2,3")])

(define_insn "iorqi3"
  [(set (match_operand:QI 0 "general_operand" "=rR,rR,Q,Q")
	(ior:QI (match_operand:QI 1 "general_operand" "%0,0,0,0")
		(match_operand:QI 2 "general_operand" "rR,Qi,rR,Qi")))]
  ""
  "bisb %2, %0")

;;- xor instructions
(define_insn "xorsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
        (xor:SI (match_operand:SI 1 "register_operand" "%0,0,0,0")
                  (match_operand:SI 2 "arith_operand" "r,I,J,K")))]
  "TARGET_40_PLUS"
  "*
{ /* Here we trust that operands don't overlap */

  unsigned int i;
  rtx lateoperands[3];

  lateoperands[0] = operands[0];
  operands[0] = gen_rtx(REG, HImode, REGNO(operands[0]) + 1);

  if (REG_P(operands[2]))
  {
    lateoperands[2] = operands[2];
    operands[2] = gen_rtx(REG, HImode, REGNO(operands[2]) + 1);
    
    output_asm_insn (\"xor %2, %0\", operands);
    output_asm_insn (\"xor %2, %0\", lateoperands);

    return \"\";
  }

  lateoperands[2] = gen_rtx(CONST_INT, VOIDmode, (INTVAL(operands[2]) >> 16) & 0xffff);
  operands[2] = gen_rtx(CONST_INT, VOIDmode, INTVAL(operands[2]) & 0xffff);
  
  if (INTVAL(operands[2]))
    output_asm_insn (\"xor %2, %0\", operands);

  if (INTVAL(lateoperands[2]))
    output_asm_insn (\"xor %2, %0\", lateoperands);

  return \"\";
}"
  [(set_attr "length" "2,1,1,2")])

(define_insn "xorhi3"
  [(set (match_operand:HI 0 "general_operand" "=rR,Q")
	(xor:HI (match_operand:HI 1 "general_operand" "%0,0")
		(match_operand:HI 2 "register_operand" "r,r")))]
  "TARGET_40_PLUS"
  "xor %2, %0"
  [(set_attr "length" "1,2")])

;;- one complement instructions

(define_insn "one_cmplhi2"
  [(set (match_operand:HI 0 "general_operand" "=rR,Q")
        (not:HI (match_operand:HI 1 "general_operand" "0,0")))]
  ""
  "com %0"
  [(set_attr "length" "1,2")])

(define_insn "one_cmplqi2"
  [(set (match_operand:QI 0 "general_operand" "=rR,Q")
        (not:QI (match_operand:QI 1 "general_operand" "0,0")))]
  ""
  "comb %0"
  [(set_attr "length" "1,2")])

;;- arithmetic shift instructions
(define_insn "ashlsi3"
  [(set (match_operand:SI 0 "register_operand" "=r,r")
	(ashift:SI (match_operand:SI 1 "register_operand" "0,0")
		   (match_operand:HI 2 "general_operand" "rR,Qi")))]
  "TARGET_45"
  "ashc %2,%0"
  [(set_attr "length" "1,2")])

;; Arithmetic right shift on the pdp works by negating the shift count.
(define_expand "ashrsi3"
  [(set (match_operand:SI 0 "register_operand" "=r")
	(ashift:SI (match_operand:SI 1 "register_operand" "0")
		   (match_operand:HI 2 "general_operand" "g")))]
  ""
  "
{
  operands[2] = negate_rtx (HImode, operands[2]);
}")

;; define asl aslb asr asrb - ashc missing!

;; asl 
(define_insn "" 
  [(set (match_operand:HI 0 "general_operand" "=rR,Q")
	(ashift:HI (match_operand:HI 1 "general_operand" "0,0")
		   (const_int 1)))]
  ""
  "asl %0"
  [(set_attr "length" "1,2")])

;; and another possibility for asr is << -1
;; might cause problems since -1 can also be encoded as 65535!
;; not in gcc2 ??? 

;; asr
(define_insn "" 
  [(set (match_operand:HI 0 "general_operand" "=rR,Q")
	(ashift:HI (match_operand:HI 1 "general_operand" "0,0")
		   (const_int -1)))]
  ""
  "asr %0"
  [(set_attr "length" "1,2")])

;; shift is by arbitrary count is expensive, 
;; shift by one cheap - so let's do that, if
;; space doesn't matter
(define_insn "" 
  [(set (match_operand:HI 0 "general_operand" "=r")
	(ashift:HI (match_operand:HI 1 "general_operand" "0")
		   (match_operand:HI 2 "expand_shift_operand" "O")))]
  "TARGET_TIME"
  "*
{
  register int i;

  for (i = 1; i <= abs(INTVAL(operands[2])); i++)
    if (INTVAL(operands[2]) < 0)
      output_asm_insn(\"asr %0\", operands);
    else
      output_asm_insn(\"asl %0\", operands);
      
  return \"\";
}"
;; longest is 4
  [(set (attr "length") (const_int 4))])

;; aslb
(define_insn "" 
  [(set (match_operand:QI 0 "general_operand" "=r,o")
	(ashift:QI (match_operand:QI 1 "general_operand" "0,0")
		   (match_operand:HI 2 "const_immediate_operand" "n,n")))]
  ""
  "*
{ /* allowing predec or post_inc is possible, but hairy! */
  int i, cnt;

  cnt = INTVAL(operands[2]) & 0x0007;

  for (i=0 ; i < cnt ; i++)
       output_asm_insn(\"aslb %0\", operands);

  return \"\";
}"
;; set attribute length ( match_dup 2 & 7 ) *(1 or 2) !!!
  [(set_attr_alternative "length" 
                         [(const_int 7)
                          (const_int 14)])])

;;; asr 
;(define_insn "" 
;  [(set (match_operand:HI 0 "general_operand" "=rR,Q")
;	(ashiftrt:HI (match_operand:HI 1 "general_operand" "0,0")
;		     (const_int 1)))]
;  ""
;  "asr %0"
;  [(set_attr "length" "1,2")])

;; asrb
(define_insn "" 
  [(set (match_operand:QI 0 "general_operand" "=r,o")
	(ashiftrt:QI (match_operand:QI 1 "general_operand" "0,0")
		     (match_operand:HI 2 "const_immediate_operand" "n,n")))]
  ""
  "*
{ /* allowing predec or post_inc is possible, but hairy! */
  int i, cnt;

  cnt = INTVAL(operands[2]) & 0x0007;

  for (i=0 ; i < cnt ; i++)
       output_asm_insn(\"asrb %0\", operands);

  return \"\";
}"
  [(set_attr_alternative "length" 
                         [(const_int 7)
                          (const_int 14)])])

;; the following is invalid - too complex!!! - just say 14 !!!
;  [(set (attr "length") (plus (and (match_dup 2)
;                                   (const_int 7))
;                              (and (match_dup 2)
;                                   (const_int 7))))])



;; can we get +-1 in the next pattern? should 
;; have been caught by previous patterns!

(define_insn "ashlhi3"
  [(set (match_operand:HI 0 "register_operand" "=r,r")
	(ashift:HI (match_operand:HI 1 "register_operand" "0,0")
		   (match_operand:HI 2 "general_operand" "rR,Qi")))]
  ""
  "*
{
  if (GET_CODE(operands[2]) == CONST_INT)
    if (INTVAL(operands[2]) == 1)
      return \"asl %0\";
    else if (INTVAL(operands[2]) == -1)
      return \"asr %0\";

  return \"ash %2,%0\";
}"
  [(set_attr "length" "1,2")])

;; Arithmetic right shift on the pdp works by negating the shift count.
(define_expand "ashrhi3"
  [(set (match_operand:HI 0 "register_operand" "=r")
	(ashift:HI (match_operand:HI 1 "register_operand" "0")
		   (match_operand:HI 2 "general_operand" "g")))]
  ""
  "
{
  operands[2] = negate_rtx (HImode, operands[2]);
}")

;;;;- logical shift instructions
;;(define_insn "lshrsi3"
;;  [(set (match_operand:HI 0 "register_operand" "=r")
;;	(lshiftrt:HI (match_operand:HI 1 "register_operand" "0")
;;		     (match_operand:HI 2 "arith_operand" "rI")))]
;;  ""
;;  "srl %0,%2")

;; absolute 

(define_insn "absdf2"
  [(set (match_operand:DF 0 "general_operand" "=fR,Q")
	(abs:DF (match_operand:DF 1 "general_operand" "0,0")))]
  "TARGET_FPU"
  "absd %0"
  [(set_attr "length" "1,2")])

(define_insn "abshi2"
  [(set (match_operand:HI 0 "general_operand" "=r,o")
	(abs:HI (match_operand:HI 1 "general_operand" "0,0")))]
  "TARGET_ABSHI_BUILTIN"
  "*
{
  static count = 0;
  char buf[200];
	
  output_asm_insn(\"tst %0\", operands);
  sprintf(buf, \"bge abshi%d\", count);
  output_asm_insn(buf, NULL);
  output_asm_insn(\"neg %0\", operands);
  sprintf(buf, \"\\nabshi%d:\", count++);
  output_asm_insn(buf, NULL);

  return \"\";
}"
  [(set_attr "length" "3,5")])


;; define expand abshi - is much better !!! - but
;; will it be optimized into an abshi2 ?
;; it will leave better code, because the tsthi might be 
;; optimized away!!
; -- just a thought - don't have time to check 
;
;(define_expand "abshi2"
;  [(match_operand:HI 0 "general_operand" "")
;   (match_operand:HI 1 "general_operand" "")]
;  ""
;  "
;{
;  rtx label = gen_label_rtx ();
;
;  /* do I need this? */
;  do_pending_stack_adjust ();
;
;  emit_move_insn (operands[0], operands[1]);
;
;  emit_insn (gen_tsthi (operands[0]));
;  emit_insn (gen_bge (label1));
;
;  emit_insn (gen_neghi(operands[0], operands[0])
;  
;  emit_barrier ();
;
;  emit_label (label);
;
;  /* allow REG_NOTES to be set on last insn (labels don't have enough
;     fields, and can't be used for REG_NOTES anyway).  */
;  emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
;  DONE;
;}")

;; negate insns

(define_insn "negdf2"
  [(set (match_operand:DF 0 "general_operand" "=fR,Q")
	(neg:DF (match_operand:DF 1 "register_operand" "0,0")))]
  "TARGET_FPU"
  "negd %0"
  [(set_attr "length" "1,2")])

(define_insn "neghi2"
  [(set (match_operand:HI 0 "general_operand" "=rR,Q")
	(neg:HI (match_operand:HI 1 "general_operand" "0,0")))]
  ""
  "neg %0"
  [(set_attr "length" "1,2")])

(define_insn "negqi2"
  [(set (match_operand:QI 0 "general_operand" "=rR,Q")
	(neg:QI (match_operand:QI 1 "general_operand" "0,0")))]
  ""
  "negb %0"
  [(set_attr "length" "1,2")])


;; Unconditional and other jump instructions
(define_insn "jump"
  [(set (pc)
	(label_ref (match_operand 0 "" "")))]
  ""
  "jmp %l0"
  [(set_attr "length" "2")])

(define_insn ""
  [(set (pc)
    (label_ref (match_operand 0 "" "")))
   (clobber (const_int 1))]
  ""
  "jmp %l0"
  [(set_attr "length" "2")])

(define_insn "tablejump"
  [(set (pc) (match_operand:HI 0 "general_operand" "rR,Q"))
   (use (label_ref (match_operand 1 "" "")))]
  ""
  "jmp %0"
  [(set_attr "length" "1,2")])

;; indirect jump - let's be conservative!
;; allow only register_operand, even though we could also 
;; allow labels etc.

(define_insn "indirect_jump"
  [(set (pc) (match_operand:HI 0 "register_operand" "r"))]
  ""
  "jmp (%0)")

;;- jump to subroutine

(define_insn "call"
  [(call (match_operand:HI 0 "general_operand" "R,Q")
	 (match_operand:HI 1 "general_operand" "g,g"))
;;   (use (reg:HI 0)) what was that ???
  ]
  ;;- Don't use operand 1 for most machines.
  ""
  "jsr pc, %0"
  [(set_attr "length" "1,2")])

;;- jump to subroutine
(define_insn "call_value"
  [(set (match_operand 0 "" "")
	(call (match_operand:HI 1 "general_operand" "R,Q")
	      (match_operand:HI 2 "general_operand" "g,g")))
;;   (use (reg:HI 0)) - what was that ????
  ]
  ;;- Don't use operand 2 for most machines.
  ""
  "jsr pc, %1"
  [(set_attr "length" "1,2")])

;;- nop instruction
(define_insn "nop"
  [(const_int 0)]
  ""
  "nop")


;;- multiply 

(define_insn "muldf3"
  [(set (match_operand:DF 0 "register_operand" "=a,a,a")
	(mult:DF (match_operand:DF 1 "register_operand" "%0,0,0")
		 (match_operand:DF 2 "general_operand" "fR,Q,F")))]
  "TARGET_FPU"
  "muld %2, %0"
  [(set_attr "length" "1,2,5")])

;; 16 bit result multiply:
;; currently we multiply only into odd registers, so we don't use two 
;; registers - but this is a bit inefficient at times. If we define 
;; a register class for each register, then we can specify properly 
;; which register need which scratch register ....

(define_insn "mulhi3"
  [(set (match_operand:HI 0 "register_operand" "=d,d") ; multiply regs
	(mult:HI (match_operand:HI 1 "register_operand" "%0,0")
		 (match_operand:HI 2 "general_operand" "rR,Qi")))]
  "TARGET_45"
  "mul %2, %0"
  [(set_attr "length" "1,2")])

;; 32 bit result
(define_expand "mulhisi3"
  [(set (match_dup 3)
	(match_operand:HI 1 "general_operand" "g,g"))
   (set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered!
	(mult:SI (truncate:HI 
                  (match_dup 0))
		 (match_operand:HI 2 "general_operand" "rR,Qi")))]
  "TARGET_45"
  "operands[3] = gen_lowpart(HImode, operands[1]);")

(define_insn ""
  [(set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered!
	(mult:SI (truncate:HI 
                  (match_operand:SI 1 "register_operand" "%0,0"))
		 (match_operand:HI 2 "general_operand" "rR,Qi")))]
  "TARGET_45"
  "mul %2, %0"
  [(set_attr "length" "1,2")])

;(define_insn "mulhisi3"
;  [(set (match_operand:SI 0 "register_operand" "=r,r") ; even numbered!
;	(mult:SI (truncate:HI 
;                  (match_operand:SI 1 "register_operand" "%0,0"))
;		 (match_operand:HI 2 "general_operand" "rR,Qi")))]
;  "TARGET_45"
;  "mul %2, %0"
;  [(set_attr "length" "1,2")])

;;- divide
;; how can I use the remainder ? - 
;; modsidi and move upper register to lower ????

(define_insn "divdf3"
  [(set (match_operand:DF 0 "register_operand" "=a,a,a")
	(div:DF (match_operand:DF 1 "register_operand" "0,0,0")
		(match_operand:DF 2 "general_operand" "fR,Q,F")))]
  "TARGET_FPU"
  "divd %2, %0"
  [(set_attr "length" "1,2,5")])

(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=r,r")
	(truncate:HI 
	 (div:SI
	  (match_operand:SI 1 "general_operand" "0,0")
	  (sign_extend:SI (match_operand:HI 2 "general_operand" "rR,Q")))))]
  "TARGET_45"
  "div %2,%0"
  [(set_attr "length" "1,2")])

;; - problem matching the (sign_extend:SI (const_int ...))
; used without -O 
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=r")
	(truncate:HI 
	 (div:SI
	  (match_operand:SI 1 "general_operand" "0")
	  (sign_extend:SI (match_operand 2 "immediate_operand" "n")))))]
  "TARGET_45"
  "div %2,%0"
  [(set_attr "length" "2")])

; used with -O
(define_insn ""
  [(set (match_operand:HI 0 "general_operand" "=r")
	(truncate:HI 
	 (div:SI
	  (match_operand:SI 1 "general_operand" "0")
	  (match_operand:SI 2 "immediate_operand" "i"))))]
  "TARGET_45"
  "div %2,%0"
  [(set_attr "length" "2")])

(define_expand "divhi3"
  [(set (match_dup 3)
	(sign_extend:SI (match_operand:HI 1 "general_operand" "g")))
   (set (match_operand:HI 0 "general_operand" "g")
	(truncate:HI 
	 (div:SI
	  (match_dup 3)
	  (sign_extend:SI (match_operand:HI 2 "general_operand" "g")))))]
  "TARGET_45"
  "operands[3] = gen_reg_rtx (SImode);")

(define_expand "udivqi"
  [(set (subreg:HI (match_dup 3) 1)
	(zero_extend:HI (match_operand:QI 1 "general_operand" "g")))
   (set (subreg:HI (match_dup 3) 0)
	(const_int 0))
   (set (match_dup 4)
	(sign_extend:HI (match_operand:QI 2 "general_operand" "g")))
   (set (match_dup 5)
	(and:HI (match_dup 4)
		(const_int 255)))
   (set (match_dup 6)
	(truncate:HI 
	 (div:SI
	  (match_dup 3)
	  (sign_extend:SI (match_dup 5)))))
   (set (match_operand:QI 0 "general_operand" "g")
	(truncate:QI (match_dup 6)))]
  "TARGET_45"
  "
{
  operands[3] = gen_reg_rtx (SImode);
  operands[4] = gen_reg_rtx (HImode);
  operands[5] = gen_reg_rtx (HImode);
  operands[6] = gen_reg_rtx (HImode);
}")
   
;; we must restrict it to divide by 15-bit constant...
(define_expand "udivhi3"
  [(set (subreg:HI (match_dup 3) 1)
	(match_operand:HI 1 "general_operand" "g"))
   (set (subreg:HI (match_dup 3) 0)
	(const_int 0))
   (set (match_operand:HI 0 "general_operand" "g")
	(truncate:HI 
	 (div:SI
	  (match_dup 3)
	  (sign_extend:SI (match_operand:HI 2 "immediate15_operand" "n")))))]
  "TARGET_45"
  "
{
  operands[3] = gen_reg_rtx (SImode);
  
  if (GET_CODE (operands[2]) != CONST_INT
      || ((INTVAL (operands[2]) & 0x8000) != 0x0000))
    FAIL;
}")

(define_insn ""
  [(set (subreg:HI (match_operand:SI 0 "general_operand" "=r,r") 1)
	(truncate:HI 
	 (mod:SI
	  (match_operand:SI 1 "general_operand" "0,0")
	  (sign_extend:SI (match_operand:HI 2 "nonimmediate_operand" "rR,Q")))))]
  "TARGET_45"
  "div %2,%0"
  [(set_attr "length" "1,2")])

;; (sign_extend:SI (const_int ))
; w/o -O
(define_insn ""
  [(set (subreg:HI (match_operand:SI 0 "general_operand" "=r") 1)
	(truncate:HI 
	 (mod:SI
	  (match_operand:SI 1 "general_operand" "0")
	  (sign_extend:SI (match_operand 2 "immediate_operand" "i")))))]
  "TARGET_45"
  "div %2,%0"
  [(set_attr "length" "2")])
; w/ -O
(define_insn ""
  [(set (subreg:HI (match_operand:SI 0 "general_operand" "=r") 1)
	(truncate:HI 
	 (mod:SI
	  (match_operand:SI 1 "general_operand" "0")
	  (match_operand:SI 2 "immediate_operand" "i"))))]
  "TARGET_45"
  "div %2,%0"
  [(set_attr "length" "2")])

(define_expand "modhi3"
  [(set (match_dup 3)
	(sign_extend:SI (match_operand:HI 1 "general_operand" "g")))
   (set (subreg:HI (match_dup 3) 1)
	(truncate:HI 
	 (mod:SI
	  (match_dup 3)
	  (sign_extend:SI (match_operand:HI 2 "general_operand" "g")))))
   (set (match_operand:HI 0 "general_operand" "g")
	(subreg:HI (match_dup 3) 1))]
  "TARGET_45"
  "operands[3] = gen_reg_rtx (SImode);")

;; we must restrict it to mod by 15 bit constant
(define_expand "umodhi3"
  [(set (subreg:HI (match_dup 3) 0)
	(match_operand:HI 1 "general_operand" "g"))
   (set (subreg:HI (match_dup 3) 1)
	(const_int 0))
   (set (subreg:HI (match_dup 3) 1)
	(truncate:HI 
	 (mod:SI
	  (match_dup 3)
	  (sign_extend:SI (match_operand:HI 2 "immediate15_operand" "n")))))
   (set (match_operand:HI 0 "general_operand" "g")
	(subreg:HI (match_dup 3) 1))]
  "TARGET_45"
  "
{
  operands[3] = gen_reg_rtx (SImode);
  
  if (GET_CODE (operands[2]) != CONST_INT
      || ((INTVAL (operands[2]) & 0x8000) != 0x0000))
    FAIL;
}")

(define_insn ""
  [(set (subreg:HI (match_operand:SI 0 "general_operand" "=r,r") 0)
	(truncate:HI 
	 (div:SI
	  (match_operand:SI 1 "general_operand" "0,0")
	  (sign_extend:SI (match_operand:HI 2 "nonimmediate_operand" "rR,Q")))))
   (set (subreg:HI (match_dup 0) 1)
	(truncate:HI 
	 (mod:SI
	  (match_dup 1)
	  (sign_extend:SI (match_dup 2)))))]
  "TARGET_45"
  "div %2, %0"
  [(set_attr "length" "1,2")])

;; (sign_extend:SI (const_int))
; w/o -O 
(define_insn ""
  [(set (subreg:HI (match_operand:SI 0 "general_operand" "=r") 0)
	(truncate:HI 
	 (div:SI
	  (match_operand:SI 1 "general_operand" "0")
	  (sign_extend:SI (match_operand 2 "immediate_operand" "i")))))
   (set (subreg:HI (match_dup 0) 1)
	(truncate:HI 
	 (mod:SI
	  (match_dup 1)
	  (sign_extend:SI (match_dup 2)))))]
  "TARGET_45"
  "div %2, %0"
  [(set_attr "length" "2")])
; w/ -O
(define_insn ""
  [(set (subreg:HI (match_operand:SI 0 "general_operand" "=r") 0)
	(truncate:HI 
	 (div:SI
	  (match_operand:SI 1 "general_operand" "0")
	  (match_operand:SI 2 "immediate_operand" "i"))))
   (set (subreg:HI (match_dup 0) 1)
	(truncate:HI 
	 (mod:SI
	  (match_dup 1)
	  (match_dup 2))))]
  "TARGET_45"
  "div %2, %0"
  [(set_attr "length" "2")])

(define_expand "divmodhi4"
  [(set (match_dup 4)
	(sign_extend:SI (match_operand:HI 1 "general_operand" "g")))
   (set (subreg:HI (match_dup 4) 0)
	(truncate:HI 
	 (div:SI
	  (match_dup 4)
	  (sign_extend:SI (match_operand:HI 2 "general_operand" "g")))))
   (set (subreg:HI (match_dup 4) 1)
	(truncate:HI 
	 (mod:SI
	  (match_dup 4)
	  (sign_extend:SI (match_dup 2)))))
   (set (match_operand:HI 0 "general_operand" "g")
	(subreg:HI (match_dup 4) 0))
   (set (match_operand:HI 3 "general_operand" "g")
	(subreg:HI (match_dup 4) 1))]
  "TARGET_45"
  "operands[4] = gen_reg_rtx(SImode);")

(define_expand "udivmodhi4"
  [(set (subreg:HI (match_dup 3) 1)
	(match_operand:HI 1 "general_operand" "g"))
   (set (subreg:HI (match_dup 3) 0)
	(const_int 0))
   (set (subreg:HI (match_dup 4) 0)
	(truncate:HI 
	 (div:SI
	  (match_dup 4)
	  (sign_extend:SI (match_operand:HI 2 "immediate15_operand" "n")))))
   (set (subreg:HI (match_dup 4) 1)
	(truncate:HI 
	 (mod:SI
	  (match_dup 4)
	  (sign_extend:SI (match_dup 2)))))
   (set (match_operand:HI 0 "general_operand" "g")
	(subreg:HI (match_dup 4) 0))
   (set (match_operand:HI 3 "general_operand" "g")
	(subreg:HI (match_dup 4) 1))]
  "TARGET_45"
  "
{
  operands[3] = gen_reg_rtx (SImode);
  
  if (GET_CODE (operands[2]) != CONST_INT
      || ((INTVAL (operands[2]) & 0x8000) != 0x0000))
    FAIL;
}")

;; truncate used in div/mod patterns
(define_insn ""
  [(set (match_operand:QI 0 "general_operand" "=r,r")
	(truncate:QI (match_operand:HI 1 "general_operand" "0,r")))]
  "TARGET_45"
  "@
   ; nop
   movb %1, %0"
  [(set_attr "length" "0,1")])
   
;; is rotate doing the right thing to be included here ????
