# vcg_shift --- generate a variety of shift-related code sequences


# mul_a_by --- generate code to multiply A_REG by a constant, quickly

   ipointer function mul_a_by (constant)
   integer constant

   ipointer gen_shift, gen_generic, seq, gen_mr, lshift_a_by

   integer ad (ADDR_DESC_SIZE)

   select (constant)
      when (-1)
         return (gen_generic (TCA_INS))
      when (0)
         return (gen_generic (CRA_INS))
      when (1)
         return (0)
      when (2)
         return (lshift_a_by (1))
      when (4)
         return (lshift_a_by (2))
      when (8)
         return (lshift_a_by (3))
      when (16)
         return (lshift_a_by (4))
      when (32)
         return (lshift_a_by (5))
      when (64)
         return (lshift_a_by (6))
      when (128)
         return (lshift_a_by (7))
      when (256)
         return (lshift_a_by (8))
      when (512)
         return (lshift_a_by (9))
      when (1024)
         return (lshift_a_by (10))
      when (2048)
         return (lshift_a_by (11))
      when (4096)
         return (lshift_a_by (12))
      when (8192)
         return (lshift_a_by (13))
      when (16384)
         return (lshift_a_by (14))

   else {
      if (constant == :100000)
         return (lshift_a_by (15))
      else {
         AD_MODE (ad) = ILIT_AM
         AD_LIT1 (ad) = constant
         return (seq (gen_mr (MPY_INS, ad), gen_generic (XCB_INS)))
         }
      }

   end



# mul_l_by --- generate code to multiply L_REG by a constant, quickly

   ipointer function mul_l_by (amount)
   long_int amount

   ipointer lshift_l_by, gen_generic, seq, gen_mr

   integer ad (ADDR_DESC_SIZE), power
   long_int kluge
   equivalence (kluge, AD_LIT1 (ad))

#  long_int powers_of_two (32)
#  data powers_of_two / _
#     0000001, 0000002, 0000004, 0000008, 0000016, 0000032, 0000064,
#     0000128, 0000256, 0000512, 0001024, 0002048, 0004096, 0008192,
#     0016384, 0032768, 0065536, 131072, 262144, 524288,
#     1048576, 2097152, 4194304, 8388608, 16777216, 33554432,
#     67108864, 134217728, 268435456, 536870912, 1073741824,
#     :20000000000/

   include POWER_COM

   for (power = 0; power <= 31; power += 1)
      if (amount == powers_of_two (power + 1))
         return (lshift_l_by (power))

   if (amount == -1)
      return (gen_generic (TCL_INS))
   else if (amount == 0)
      return (gen_generic (CRL_INS))
   else {
      AD_MODE (ad) = LLIT_AM
      kluge = amount
      return (seq (gen_mr (MPL_INS, ad), gen_generic (ILE_INS)))
      }

   end



# div_a_by --- generate code to divide A_REG by a constant, quickly

   ipointer function div_a_by (amount, mode)
   integer amount
   integer mode      # INT or UNS

   ipointer rshift_a_by, seq, gen_generic, gen_mr

   integer ad (ADDR_DESC_SIZE)

   select (amount)
      when (-1)
         return (gen_generic (TCA_INS))
      when (0) {
         call warning ("divide by zero in input*n"p)
         return (0)
         }
      when (1)
         return (0)

   if (mode == UNS_MODE)      # we can try for a logical right shift
      select (amount)
         when (2)
            return (rshift_a_by (1))
         when (4)
            return (rshift_a_by (2))
         when (8)
            return (rshift_a_by (3))
         when (16)
            return (rshift_a_by (4))
         when (32 )
            return (rshift_a_by (5))
         when (64)
            return (rshift_a_by (6))
         when (128)
            return (rshift_a_by (7))
         when (256)
            return (rshift_a_by (8))
         when (512)
            return (rshift_a_by (9))
         when (1024)
            return (rshift_a_by (10))
         when (2048)
            return (rshift_a_by (11))
         when (4096)
            return (rshift_a_by (12))
         when (8192)
            return (rshift_a_by (13))
         when (16384)
            return (rshift_a_by (14))
         when (-32768)
            return (rshift_a_by (15))

   # Sigh.  Have to generate a division operation.
   AD_MODE (ad) = ILIT_AM
   AD_LIT1 (ad) = amount
   if (mode == INT_MODE)
      return (seq (gen_generic (PIDA_INS), gen_mr (DIV_INS, ad)))
   else
      return (seq (gen_generic (XCA_INS), gen_mr (DIV_INS, ad)))

   end



# div_l_by --- generate code to divide L_REG by a constant, quickly

   ipointer function div_l_by (amount, mode)
   long_int amount
   integer mode      # INT_MODE or UNS_MODE

   ipointer rshift_l_by, gen_generic, seq, gen_mr

   integer ad (ADDR_DESC_SIZE), power
   long_int kluge
   equivalence (kluge, AD_LIT1 (ad))

#  long_int powers_of_two (32)
#  data powers_of_two / _
#     0000001, 0000002, 0000004, 0000008, 0000016, 0000032, 0000064,
#     0000128, 0000256, 0000512, 0001024, 0002048, 0004096, 0008192,
#     0016384, 0032768, 0065536, 131072, 262144, 524288,
#     1048576, 2097152, 4194304, 8388608, 16777216, 33554432,
#     67108864, 134217728, 268435456, 536870912, 1073741824,
#     :20000000000/

   include POWER_COM

   if (mode == UNS_MODE)      # we can logical right shift for powers of 2
      for (power = 0; power <= 31; power += 1)
         if (amount == powers_of_two (power + 1))
            return (rshift_l_by (power))

   if (amount == -1)
      return (gen_generic (TCL_INS))
   else if (amount == 0) {
      call warning ("divide by zero in input*n"p)
      return (0)
      }
   else if (amount == 1)
      return (0)
   else {
      AD_MODE (ad) = LLIT_AM
      kluge = amount
      if (mode == INT_MODE)
         return (seq (gen_generic (PIDL_INS), gen_mr (DVL_INS, ad)))
      return (seq (gen_generic (ILE_INS), gen_generic (CRL_INS),
         gen_mr (DVL_INS, ad)))
      }

   end



# lshift_a_by --- generate code to shift A_REG left, quickly

   ipointer function lshift_a_by (amount)
   integer amount

   include VCG_COMMON

   ipointer gen_shift, gen_generic, seq

   if (amount > 15)
      return (gen_generic (CRA_INS))

   if (amount < 0) {
      call warning ("lshift_a_by: shift count < 0*n"p)
      return (0)
      }

   select (amount)
      when (0)
         return (0)
      when (1, 2, 3, 4, 5, 6, 7)
         return (gen_shift (ALL_INS, amount))
      when (8)
         return (gen_generic (ICR_INS))
      when (9, 10, 11, 12, 13, 14, 15)
         return (seq (gen_generic (ICR_INS),
            gen_shift (ALL_INS, amount - 8)))
   else
      call panic ("lshift_a_by: shift count > 15*n"p)

   end




# lshift_l_by --- generate code to shift L_REG left, quickly

   ipointer function lshift_l_by (amount)
   integer amount

   include VCG_COMMON

   ipointer gen_shift, gen_generic, seq

   if (amount > 31)
      return (gen_generic (CRL_INS))

   if (amount < 0) {
      call warning ("lshift_l_by: shift count < 0*n"p)
      return (0)
      }

   select (amount)
      when (0)
         return (0)
      when (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
         return (gen_shift (LLL_INS, amount))
      when (16)
         return (gen_generic (XCB_INS))
      when (17, 18, 19, 20, 21, 22, 23)
         return (seq (gen_generic (XCB_INS),
            gen_shift (ALL_INS, amount - 16)))
      when (24)
         return (seq (gen_generic (XCB_INS), gen_generic (ICR_INS)))
      when (25, 26, 27, 28, 29, 30, 31)
         return (seq (gen_generic (XCB_INS), gen_generic (ICR_INS),
            gen_shift (ALL_INS, amount - 24)))
   else
      call panic ("lshift_l_by: shift count > 31*n"p)

   end




# rshift_a_by --- generate code to shift A_REG right, quickly

   ipointer function rshift_a_by (amount)
   integer amount

   include VCG_COMMON

   ipointer gen_shift, gen_generic, seq

   if (amount > 15)
      return (gen_generic (CRA_INS))

   if (amount < 0) {
      call warning ("rshift_a_by: shift count < 0*n"p)
      return (0)
      }

   select (amount)
      when (0)
         return (0)
      when (1, 2, 3, 4, 5, 6, 7)
         return (gen_shift (ARL_INS, amount))
      when (8)
         return (gen_generic (ICL_INS))
      when (9, 10, 11, 12, 13, 14, 15)
         return (seq (gen_generic (ICL_INS),
            gen_shift (ARL_INS, amount - 8)))
   else
      call panic ("rshift_a_by: shift count > 15*n"p)

   end



# rshift_l_by --- generate code to shift A_REG right, quickly

   ipointer function rshift_l_by (amount)
   integer amount

   include VCG_COMMON

   ipointer gen_shift, gen_generic, seq

   if (amount > 31)
      return (gen_generic (CRL_INS))

   if (amount < 0) {
      call warning ("rshift_l_by: shift count < 0*n"p)
      return (0)
      }

   select (amount)
      when (0)
         return (0)
      when (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
         return (gen_shift (LRL_INS, amount))
      when (16)
         return (gen_generic (XCA_INS))
      when (17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31)
         return (seq (gen_generic (XCA_INS),
            gen_shift (LRL_INS, amount - 16)))
   else
      call panic ("rshift_l_by: shift count > 31*n"p)

   end



# arshift_a_by --- arithmetic right shift A_REG, quickly

   ipointer function arshift_a_by (amount)
   integer amount

   ipointer gen_shift

   if (amount < 0 || amount > 16) {
      call warning ("arshift_a_by: shift count out of range*n"p)
      return (0)
      }

   if (amount == 0)
      return (0)

   return (gen_shift (ARS_INS, amount))
   end



# arshift_l_by --- arithmetic right shift L_REG, quickly

   ipointer function arshift_l_by (amount)
   integer amount

   ipointer gen_shift

   if (amount < 0 || amount > 32) {
      call warning ("arshift_l_by: shift count out of range*n"p)
      return (0)
      }

   if (amount == 0)
      return (0)

   return (gen_shift (LRS_INS, amount))
   end
