]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - opcodes/fr30-dis.c
* config/sh/tm-sh.h (BELIEVE_PCC_PROMOTION): Define, so that
[thirdparty/binutils-gdb.git] / opcodes / fr30-dis.c
index d2cbab3d16720557c7aec77d69793e8280faeb64..f171395cbeafe912ab53b62c98743f21b65a8548 100644 (file)
@@ -40,9 +40,13 @@ along with this program; if not, write to the Free Software Foundation, Inc.,
 /* Default text to print if an instruction isn't recognized.  */
 #define UNKNOWN_INSN_MSG _("*unknown*")
 
+/* Used by the ifield rtx function.  */
+#define FLD(f) (fields->f)
+
 static int extract_normal
-     PARAMS ((CGEN_OPCODE_DESC, CGEN_EXTRACT_INFO *, CGEN_INSN_BYTES,
-             unsigned int, int, int, int, long *));
+     PARAMS ((CGEN_OPCODE_DESC, CGEN_EXTRACT_INFO *, CGEN_INSN_INT,
+             unsigned int, unsigned int, unsigned int, unsigned int,
+             unsigned int, unsigned int, bfd_vma, long *));
 static void print_normal
      PARAMS ((CGEN_OPCODE_DESC, PTR, long, unsigned int, bfd_vma, int));
 static void print_address
@@ -51,7 +55,7 @@ static void print_keyword
      PARAMS ((CGEN_OPCODE_DESC, PTR, CGEN_KEYWORD *, long, unsigned int));
 static int extract_insn_normal
      PARAMS ((CGEN_OPCODE_DESC, const CGEN_INSN *, CGEN_EXTRACT_INFO *,
-             unsigned long, CGEN_FIELDS *, bfd_vma));
+             CGEN_INSN_INT, CGEN_FIELDS *, bfd_vma));
 static void print_insn_normal
      PARAMS ((CGEN_OPCODE_DESC, PTR, const CGEN_INSN *, CGEN_FIELDS *,
              bfd_vma, int));
@@ -61,6 +65,107 @@ static int default_print_insn
      PARAMS ((CGEN_OPCODE_DESC, bfd_vma, disassemble_info *));
 \f
 /* -- disassembler routines inserted here */
+/* -- dis.c */
+
+static void
+print_register_list (dis_info, value, offset, load_store)
+     PTR dis_info;
+     long value;
+     long offset;
+     int load_store; /* 0 == load, 1 == store */
+{
+  disassemble_info *info = dis_info;
+  int mask;
+  int index = 0;
+  char* comma = "";
+
+  if (load_store)
+    mask = 0x80;
+  else
+    mask = 1;
+
+  if (value & mask)
+    {
+      (*info->fprintf_func) (info->stream, "r%i", index + offset);
+      comma = ",";
+    }
+    
+  for (index = 1; index <= 7; ++index)
+    {
+      if (load_store)
+       mask >>= 1;
+      else
+       mask <<= 1;
+
+      if (value & mask)
+       {
+         (*info->fprintf_func) (info->stream, "%sr%i", comma, index + offset);
+         comma = ",";
+       }
+    }
+}
+
+static void
+print_hi_register_list_ld (od, dis_info, value, attrs, pc, length)
+     CGEN_OPCODE_DESC od;
+     PTR dis_info;
+     long value;
+     unsigned int attrs;
+     bfd_vma pc;
+     int length;
+{
+  print_register_list (dis_info, value, 8, 0/*load*/);
+}
+
+static void
+print_low_register_list_ld (od, dis_info, value, attrs, pc, length)
+     CGEN_OPCODE_DESC od;
+     PTR dis_info;
+     long value;
+     unsigned int attrs;
+     bfd_vma pc;
+     int length;
+{
+  print_register_list (dis_info, value, 0, 0/*load*/);
+}
+
+static void
+print_hi_register_list_st (od, dis_info, value, attrs, pc, length)
+     CGEN_OPCODE_DESC od;
+     PTR dis_info;
+     long value;
+     unsigned int attrs;
+     bfd_vma pc;
+     int length;
+{
+  print_register_list (dis_info, value, 8, 1/*store*/);
+}
+
+static void
+print_low_register_list_st (od, dis_info, value, attrs, pc, length)
+     CGEN_OPCODE_DESC od;
+     PTR dis_info;
+     long value;
+     unsigned int attrs;
+     bfd_vma pc;
+     int length;
+{
+  print_register_list (dis_info, value, 0, 1/*store*/);
+}
+
+static void
+print_m4 (od, dis_info, value, attrs, pc, length)
+     CGEN_OPCODE_DESC od;
+     PTR dis_info;
+     long value;
+     unsigned int attrs;
+     bfd_vma pc;
+     int length;
+{
+  disassemble_info *info = (disassemble_info *) dis_info;
+  (*info->fprintf_func) (info->stream, "%ld", value);
+}
+/* -- */
 
 /* Main entry point for operand extraction.
 
@@ -81,19 +186,178 @@ fr30_cgen_extract_operand (od, opindex, ex_info, insn_value, fields, pc)
      CGEN_OPCODE_DESC od;
      int opindex;
      CGEN_EXTRACT_INFO *ex_info;
-     CGEN_INSN_BYTES insn_value;
+     CGEN_INSN_INT insn_value;
      CGEN_FIELDS * fields;
      bfd_vma pc;
 {
   int length;
+  unsigned int total_length = CGEN_FIELDS_BITSIZE (fields);
 
   switch (opindex)
     {
     case FR30_OPERAND_RI :
-      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 12, 4, CGEN_FIELDS_BITSIZE (fields), & fields->f_Ri);
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 12, 4, 16, total_length, pc, & fields->f_Ri);
       break;
     case FR30_OPERAND_RJ :
-      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 8, 4, CGEN_FIELDS_BITSIZE (fields), & fields->f_Rj);
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 4, 16, total_length, pc, & fields->f_Rj);
+      break;
+    case FR30_OPERAND_RIC :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 16, 12, 4, 16, total_length, pc, & fields->f_Ric);
+      break;
+    case FR30_OPERAND_RJC :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 16, 8, 4, 16, total_length, pc, & fields->f_Rjc);
+      break;
+    case FR30_OPERAND_CRI :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 16, 12, 4, 16, total_length, pc, & fields->f_CRi);
+      break;
+    case FR30_OPERAND_CRJ :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 16, 8, 4, 16, total_length, pc, & fields->f_CRj);
+      break;
+    case FR30_OPERAND_RS1 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 4, 16, total_length, pc, & fields->f_Rs1);
+      break;
+    case FR30_OPERAND_RS2 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 12, 4, 16, total_length, pc, & fields->f_Rs2);
+      break;
+    case FR30_OPERAND_R13 :
+      length = extract_normal (od, ex_info, insn_value, 0, 0, 0, 0, 0, total_length, pc, & fields->f_nil);
+      break;
+    case FR30_OPERAND_R14 :
+      length = extract_normal (od, ex_info, insn_value, 0, 0, 0, 0, 0, total_length, pc, & fields->f_nil);
+      break;
+    case FR30_OPERAND_R15 :
+      length = extract_normal (od, ex_info, insn_value, 0, 0, 0, 0, 0, total_length, pc, & fields->f_nil);
+      break;
+    case FR30_OPERAND_PS :
+      length = extract_normal (od, ex_info, insn_value, 0, 0, 0, 0, 0, total_length, pc, & fields->f_nil);
+      break;
+    case FR30_OPERAND_U4 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 4, 16, total_length, pc, & fields->f_u4);
+      break;
+    case FR30_OPERAND_U4C :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), 0, 12, 4, 16, total_length, pc, & fields->f_u4c);
+      break;
+    case FR30_OPERAND_U8 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & fields->f_u8);
+      break;
+    case FR30_OPERAND_I8 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), 0, 4, 8, 16, total_length, pc, & fields->f_i8);
+      break;
+    case FR30_OPERAND_UDISP6 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 4, 16, total_length, pc, & value);
+        value = ((value) << (2));
+        fields->f_udisp6 = value;
+      }
+      break;
+    case FR30_OPERAND_DISP8 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX), 0, 4, 8, 16, total_length, pc, & fields->f_disp8);
+      break;
+    case FR30_OPERAND_DISP9 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX), 0, 4, 8, 16, total_length, pc, & value);
+        value = ((value) << (1));
+        fields->f_disp9 = value;
+      }
+      break;
+    case FR30_OPERAND_DISP10 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX), 0, 4, 8, 16, total_length, pc, & value);
+        value = ((value) << (2));
+        fields->f_disp10 = value;
+      }
+      break;
+    case FR30_OPERAND_S10 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX), 0, 8, 8, 16, total_length, pc, & value);
+        value = ((value) << (2));
+        fields->f_s10 = value;
+      }
+      break;
+    case FR30_OPERAND_U10 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & value);
+        value = ((value) << (2));
+        fields->f_u10 = value;
+      }
+      break;
+    case FR30_OPERAND_I32 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_SIGN_OPT)|(1<<CGEN_OPERAND_UNSIGNED), 16, 0, 32, 32, total_length, pc, & fields->f_i32);
+      break;
+    case FR30_OPERAND_M4 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 4, 16, total_length, pc, & value);
+        value = ((value) | (((-1) << (4))));
+        fields->f_m4 = value;
+      }
+      break;
+    case FR30_OPERAND_I20 :
+      {
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED)|(1<<CGEN_OPERAND_VIRTUAL), 0, 8, 4, 16, total_length, pc, & fields->f_i20_4);
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED)|(1<<CGEN_OPERAND_VIRTUAL), 16, 0, 16, 16, total_length, pc, & fields->f_i20_16);
+do {
+  FLD (f_i20) = ((((FLD (f_i20_4)) << (16))) | (FLD (f_i20_16)));
+} while (0);
+      }
+      break;
+    case FR30_OPERAND_DIR8 :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & fields->f_dir8);
+      break;
+    case FR30_OPERAND_DIR9 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & value);
+        value = ((value) << (1));
+        fields->f_dir9 = value;
+      }
+      break;
+    case FR30_OPERAND_DIR10 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & value);
+        value = ((value) << (2));
+        fields->f_dir10 = value;
+      }
+      break;
+    case FR30_OPERAND_LABEL9 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_PCREL_ADDR), 0, 8, 8, 16, total_length, pc, & value);
+        value = ((((value) << (1))) + (((pc) + (2))));
+        fields->f_rel9 = value;
+      }
+      break;
+    case FR30_OPERAND_LABEL12 :
+      {
+        long value;
+        length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_PCREL_ADDR), 0, 5, 11, 16, total_length, pc, & value);
+        value = ((((value) << (1))) + (((pc) + (2))));
+        fields->f_rel12 = value;
+      }
+      break;
+    case FR30_OPERAND_REGLIST_LOW_LD :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & fields->f_reglist_low_ld);
+      break;
+    case FR30_OPERAND_REGLIST_HI_LD :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & fields->f_reglist_hi_ld);
+      break;
+    case FR30_OPERAND_REGLIST_LOW_ST :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & fields->f_reglist_low_st);
+      break;
+    case FR30_OPERAND_REGLIST_HI_ST :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 8, 8, 16, total_length, pc, & fields->f_reglist_hi_st);
+      break;
+    case FR30_OPERAND_CC :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_UNSIGNED), 0, 4, 4, 16, total_length, pc, & fields->f_cc);
+      break;
+    case FR30_OPERAND_CCC :
+      length = extract_normal (od, ex_info, insn_value, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), 16, 0, 8, 16, total_length, pc, & fields->f_ccc);
       break;
 
     default :
@@ -138,6 +402,108 @@ fr30_cgen_print_operand (od, opindex, info, fields, attrs, pc, length)
     case FR30_OPERAND_RJ :
       print_keyword (od, info, & fr30_cgen_opval_h_gr, fields->f_Rj, 0|(1<<CGEN_OPERAND_UNSIGNED));
       break;
+    case FR30_OPERAND_RIC :
+      print_keyword (od, info, & fr30_cgen_opval_h_gr, fields->f_Ric, 0|(1<<CGEN_OPERAND_UNSIGNED));
+      break;
+    case FR30_OPERAND_RJC :
+      print_keyword (od, info, & fr30_cgen_opval_h_gr, fields->f_Rjc, 0|(1<<CGEN_OPERAND_UNSIGNED));
+      break;
+    case FR30_OPERAND_CRI :
+      print_keyword (od, info, & fr30_cgen_opval_h_cr, fields->f_CRi, 0|(1<<CGEN_OPERAND_UNSIGNED));
+      break;
+    case FR30_OPERAND_CRJ :
+      print_keyword (od, info, & fr30_cgen_opval_h_cr, fields->f_CRj, 0|(1<<CGEN_OPERAND_UNSIGNED));
+      break;
+    case FR30_OPERAND_RS1 :
+      print_keyword (od, info, & fr30_cgen_opval_h_dr, fields->f_Rs1, 0|(1<<CGEN_OPERAND_UNSIGNED));
+      break;
+    case FR30_OPERAND_RS2 :
+      print_keyword (od, info, & fr30_cgen_opval_h_dr, fields->f_Rs2, 0|(1<<CGEN_OPERAND_UNSIGNED));
+      break;
+    case FR30_OPERAND_R13 :
+      print_keyword (od, info, & fr30_cgen_opval_h_r13, fields->f_nil, 0);
+      break;
+    case FR30_OPERAND_R14 :
+      print_keyword (od, info, & fr30_cgen_opval_h_r14, fields->f_nil, 0);
+      break;
+    case FR30_OPERAND_R15 :
+      print_keyword (od, info, & fr30_cgen_opval_h_r15, fields->f_nil, 0);
+      break;
+    case FR30_OPERAND_PS :
+      print_keyword (od, info, & fr30_cgen_opval_h_ps, fields->f_nil, 0);
+      break;
+    case FR30_OPERAND_U4 :
+      print_normal (od, info, fields->f_u4, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_U4C :
+      print_normal (od, info, fields->f_u4c, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_U8 :
+      print_normal (od, info, fields->f_u8, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_I8 :
+      print_normal (od, info, fields->f_i8, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_UDISP6 :
+      print_normal (od, info, fields->f_udisp6, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_DISP8 :
+      print_normal (od, info, fields->f_disp8, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
+      break;
+    case FR30_OPERAND_DISP9 :
+      print_normal (od, info, fields->f_disp9, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
+      break;
+    case FR30_OPERAND_DISP10 :
+      print_normal (od, info, fields->f_disp10, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
+      break;
+    case FR30_OPERAND_S10 :
+      print_normal (od, info, fields->f_s10, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
+      break;
+    case FR30_OPERAND_U10 :
+      print_normal (od, info, fields->f_u10, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_I32 :
+      print_normal (od, info, fields->f_i32, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_SIGN_OPT)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_M4 :
+      print_m4 (od, info, fields->f_m4, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_I20 :
+      print_normal (od, info, fields->f_i20, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED)|(1<<CGEN_OPERAND_VIRTUAL), pc, length);
+      break;
+    case FR30_OPERAND_DIR8 :
+      print_normal (od, info, fields->f_dir8, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_DIR9 :
+      print_normal (od, info, fields->f_dir9, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_DIR10 :
+      print_normal (od, info, fields->f_dir10, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_LABEL9 :
+      print_address (od, info, fields->f_rel9, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
+      break;
+    case FR30_OPERAND_LABEL12 :
+      print_address (od, info, fields->f_rel12, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
+      break;
+    case FR30_OPERAND_REGLIST_LOW_LD :
+      print_low_register_list_ld (od, info, fields->f_reglist_low_ld, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_REGLIST_HI_LD :
+      print_hi_register_list_ld (od, info, fields->f_reglist_hi_ld, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_REGLIST_LOW_ST :
+      print_low_register_list_st (od, info, fields->f_reglist_low_st, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_REGLIST_HI_ST :
+      print_hi_register_list_st (od, info, fields->f_reglist_hi_st, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_CC :
+      print_normal (od, info, fields->f_cc, 0|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
+    case FR30_OPERAND_CCC :
+      print_normal (od, info, fields->f_ccc, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_UNSIGNED), pc, length);
+      break;
 
     default :
       /* xgettext:c-format */
@@ -169,21 +535,68 @@ fr30_cgen_init_dis (od)
 \f
 #if ! CGEN_INT_INSN_P
 
+/* Subroutine of extract_normal.
+   Ensure sufficient bytes are cached in EX_INFO.
+   OFFSET is the offset in bytes from the start of the insn of the value.
+   BYTES is the length of the needed value.
+   Returns 1 for success, 0 for failure.  */
+
+static INLINE int
+fill_cache (od, ex_info, offset, bytes, pc)
+     CGEN_OPCODE_DESC od;
+     CGEN_EXTRACT_INFO *ex_info;
+     int offset, bytes;
+     bfd_vma pc;
+{
+  /* It's doubtful that the middle part has already been fetched so
+     we don't optimize that case.  kiss.  */
+  int mask;
+  disassemble_info *info = (disassemble_info *) ex_info->dis_info;
+
+  /* First do a quick check.  */
+  mask = (1 << bytes) - 1;
+  if (((ex_info->valid >> offset) & mask) == mask)
+    return 1;
+
+  /* Search for the first byte we need to read.  */
+  for (mask = 1 << offset; bytes > 0; --bytes, ++offset, mask <<= 1)
+    if (! (mask & ex_info->valid))
+      break;
+
+  if (bytes)
+    {
+      int status;
+
+      pc += offset;
+      status = (*info->read_memory_func)
+       (pc, ex_info->insn_bytes + offset, bytes, info);
+
+      if (status != 0)
+       {
+         (*info->memory_error_func) (status, pc, info);
+         return 0;
+       }
+
+      ex_info->valid |= ((1 << bytes) - 1) << offset;
+    }
+
+  return 1;
+}
+
 /* Subroutine of extract_normal.  */
 
 static INLINE long
-extract_1 (od, ex_info, start, length, word_length, bufp)
+extract_1 (od, ex_info, start, length, word_length, bufp, pc)
      CGEN_OPCODE_DESC od;
-     CGEN_EXTRACT_INFO *info;
+     CGEN_EXTRACT_INFO *ex_info;
      int start,length,word_length;
      unsigned char *bufp;
+     bfd_vma pc;
 {
   unsigned long x,mask;
   int shift;
   int big_p = CGEN_OPCODE_INSN_ENDIAN (od) == CGEN_ENDIAN_BIG;
 
-  /* FIXME: Need to use ex_info to ensure bytes have been fetched.  */
-
   switch (word_length)
     {
     case 8:
@@ -199,9 +612,9 @@ extract_1 (od, ex_info, start, length, word_length, bufp)
       /* ??? This may need reworking as these cases don't necessarily
         want the first byte and the last two bytes handled like this.  */
       if (big_p)
-       x = (bfd_getb8 (bufp) << 16) | bfd_getb16 (bufp + 1);
+       x = (bufp[0] << 16) | bfd_getb16 (bufp + 1);
       else
-       x = bfd_getl16 (bufp) | (bfd_getb8 (bufp + 2) << 16);
+       x = bfd_getl16 (bufp) | (bufp[2] << 16);
       break;
     case 32:
       if (big_p)
@@ -216,7 +629,7 @@ extract_1 (od, ex_info, start, length, word_length, bufp)
   /* Written this way to avoid undefined behaviour.  */
   mask = (((1L << (length - 1)) - 1) << 1) | 1;
   if (CGEN_INSN_LSB0_P)
-    shift = start;
+    shift = (start + 1) - length;
   else
     shift = (word_length - (start + length));
   return (x >> shift) & mask;
@@ -226,22 +639,36 @@ extract_1 (od, ex_info, start, length, word_length, bufp)
 
 /* Default extraction routine.
 
-   ATTRS is a mask of the boolean attributes.  We only need `unsigned',
-   but for generality we take a bitmask of all of them.  */
+   INSN_VALUE is the first CGEN_BASE_INSN_SIZE bits of the insn in host order,
+   or sometimes less for cases like the m32r where the base insn size is 32
+   but some insns are 16 bits.
+   ATTRS is a mask of the boolean attributes.  We only need `UNSIGNED',
+   but for generality we take a bitmask of all of them.
+   WORD_OFFSET is the offset in bits from the start of the insn of the value.
+   WORD_LENGTH is the length of the word in bits in which the value resides.
+   START is the starting bit number in the word, architecture origin.
+   LENGTH is the length of VALUE in bits.
+   TOTAL_LENGTH is the total length of the insn in bits.
+
+   Returns 1 for success, 0 for failure.  */
+
+/* ??? The return code isn't properly used.  wip.  */
 
 /* ??? This doesn't handle bfd_vma's.  Create another function when
    necessary.  */
 
 static int
-extract_normal (od, ex_info, insn_value, attrs, start, length, total_length, valuep)
+extract_normal (od, ex_info, insn_value, attrs, word_offset, start, length,
+               word_length, total_length, pc, valuep)
      CGEN_OPCODE_DESC od;
      CGEN_EXTRACT_INFO *ex_info;
-     CGEN_INSN_BYTES insn_value;
+     CGEN_INSN_INT insn_value;
      unsigned int attrs;
-     int start, length, total_length;
+     unsigned int word_offset, start, length, word_length, total_length;
+     bfd_vma pc;
      long *valuep;
 {
-  unsigned long value;
+  CGEN_INSN_INT value;
 
   /* If LENGTH is zero, this operand doesn't contribute to the value
      so give it a standard value of zero.  */
@@ -251,100 +678,58 @@ extract_normal (od, ex_info, insn_value, attrs, start, length, total_length, val
       return 1;
     }
 
-#if CGEN_INT_INSN_P
-
-  {
-    /* Written this way to avoid undefined behaviour.  */
-    unsigned long mask = (((1L << (length - 1)) - 1) << 1) | 1;
-
-    if (CGEN_INSN_LSB0_P)
-      value = insn_value >> start;
-    else
-      value = insn_value >> (total_length - (start + length));
-    value &= mask;
-    /* sign extend? */
-    if (! (attrs & CGEN_ATTR_MASK (CGEN_OPERAND_UNSIGNED))
-       && (value & (1L << (length - 1))))
-      value |= ~mask;
-  }
-
-#else
+  if (CGEN_INT_INSN_P
+      && word_offset != 0)
+    abort ();
 
-  /* The hard case is probably too slow for the normal cases.
-     It's certainly more difficult to understand than the normal case.
-     Thus this is split into two.  Keep it that way.  The hard case is defined
-     to be when a field straddles a (loosely defined) word boundary
-     (??? which may require target specific help to determine).  */
+  if (word_length > 32)
+    abort ();
 
-#if 0 /*wip*/
+  /* For architectures with insns smaller than the insn-base-bitsize,
+     word_length may be too big.  */
+#if CGEN_MIN_INSN_BITSIZE < CGEN_BASE_INSN_BITSIZE
+  if (word_offset == 0
+      && word_length > total_length)
+    word_length = total_length;
+#endif
 
-#define HARD_CASE_P 0 /* FIXME:wip */
+  /* Does the value reside in INSN_VALUE?  */
 
-  if (HARD_CASE_P)
+  if (word_offset == 0)
     {
+      /* Written this way to avoid undefined behaviour.  */
+      CGEN_INSN_INT mask = (((1L << (length - 1)) - 1) << 1) | 1;
+
+      if (CGEN_INSN_LSB0_P)
+       value = insn_value >> ((start + 1) - length);
+      else
+       value = insn_value >> (word_length - (start + length));
+      value &= mask;
+      /* sign extend? */
+      if (! CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_UNSIGNED)
+         && (value & (1L << (length - 1))))
+       value |= ~mask;
     }
-#endif
+
+#if ! CGEN_INT_INSN_P
+
   else
     {
-      unsigned char *bufp = (unsigned char *) insn_value;
+      unsigned char *bufp = ex_info->insn_bytes + word_offset / 8;
 
-      if (length > 32)
+      if (word_length > 32)
        abort ();
 
-      /* Adjust start,total_length,bufp to point to the pseudo-word that holds
-        the value.  For example in a 48 bit insn where the value to insert
-        (say an immediate value) is the last 16 bits then word_length here
-        would be 16.  To handle a 24 bit insn with an 18 bit immediate,
-        extract_1 handles 24 bits (using a combination of bfd_get8,16).  */
-
-      if (total_length > 32)
-       {
-         int needed_width = start % 8 + length;
-         int fetch_length = (needed_width <= 8 ? 8
-                             : needed_width <= 16 ? 16
-                             : 32);
-
-         if (CGEN_INSN_LSB0_P)
-           {
-             if (CGEN_INSN_WORD_ENDIAN (od) == CGEN_ENDIAN_BIG)
-               {
-                 abort (); /* wip */
-               }
-             else
-               {
-                 int offset = start & ~7;
-
-                 bufp += offset / 8;
-                 start -= offset;
-                 total_length -= offset;
-               }
-           }
-         else
-           {
-             if (CGEN_INSN_WORD_ENDIAN (od) == CGEN_ENDIAN_BIG)
-               {
-                 int offset = start & ~7;
-
-                 bufp += offset / 8;
-                 start -= offset;
-                 total_length -= offset;
-               }
-             else
-               {
-                 abort (); /* wip */
-               }
-           }
-       }
+      if (fill_cache (od, ex_info, word_offset / 8, word_length / 8, pc) == 0)
+       return 0;
 
-      /* FIXME: which bytes are being extracted have been lost.  */
-      value = extract_1 (od, ex_info, start, length, total_length, bufp);
+      value = extract_1 (od, ex_info, start, length, word_length, bufp, pc);
     }
 
 #endif /* ! CGEN_INT_INSN_P */
 
   *valuep = value;
 
-  /* FIXME: for now */
   return 1;
 }
 
@@ -438,7 +823,7 @@ extract_insn_normal (od, insn, ex_info, insn_value, fields, pc)
      CGEN_OPCODE_DESC od;
      const CGEN_INSN *insn;
      CGEN_EXTRACT_INFO *ex_info;
-     unsigned long insn_value;
+     CGEN_INSN_INT insn_value;
      CGEN_FIELDS *fields;
      bfd_vma pc;
 {
@@ -525,7 +910,7 @@ print_insn (od, pc, info, buf, buflen)
 
   ex_info.dis_info = info;
   ex_info.valid = (1 << CGEN_BASE_INSN_SIZE) - 1;
-  ex_info.bytes = buf;
+  ex_info.insn_bytes = buf;
 
   switch (buflen)
     {
@@ -561,7 +946,8 @@ print_insn (od, pc, info, buf, buflen)
       /* Basic bit mask must be correct.  */
       /* ??? May wish to allow target to defer this check until the extract
         handler.  */
-      if ((insn_value & CGEN_INSN_MASK (insn)) == CGEN_INSN_VALUE (insn))
+      if ((insn_value & CGEN_INSN_BASE_MASK (insn))
+         == CGEN_INSN_BASE_VALUE (insn))
        {
          /* Printing is handled in two passes.  The first pass parses the
             machine insn and extracts the fields.  The second pass prints