xref: /openbsd-src/gnu/usr.bin/binutils/opcodes/cgen-dis.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /* CGEN generic disassembler support code.
2 
3    Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
4 
5    This file is part of the GNU Binutils and GDB, the GNU debugger.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License along
18    with this program; if not, write to the Free Software Foundation, Inc.,
19    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20 
21 #include "sysdep.h"
22 #include <stdio.h>
23 #include "ansidecl.h"
24 #include "libiberty.h"
25 #include "bfd.h"
26 #include "symcat.h"
27 #include "opcode/cgen.h"
28 
29 /* Subroutine of build_dis_hash_table to add INSNS to the hash table.
30 
31    COUNT is the number of elements in INSNS.
32    ENTSIZE is sizeof (CGEN_IBASE) for the target.
33    ??? No longer used but leave in for now.
34    HTABLE points to the hash table.
35    HENTBUF is a pointer to sufficiently large buffer of hash entries.
36    The result is a pointer to the next entry to use.
37 
38    The table is scanned backwards as additions are made to the front of the
39    list and we want earlier ones to be prefered.  */
40 
41 static CGEN_INSN_LIST *
42 hash_insn_array (cd, insns, count, entsize, htable, hentbuf)
43      CGEN_CPU_DESC cd;
44      const CGEN_INSN * insns;
45      int count;
46      int entsize;
47      CGEN_INSN_LIST ** htable;
48      CGEN_INSN_LIST * hentbuf;
49 {
50   int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
51   int i;
52 
53   for (i = count - 1; i >= 0; --i, ++hentbuf)
54     {
55       unsigned int hash;
56       char buf [4];
57       unsigned long value;
58       const CGEN_INSN *insn = &insns[i];
59 
60       if (! (* cd->dis_hash_p) (insn))
61 	continue;
62 
63       /* We don't know whether the target uses the buffer or the base insn
64 	 to hash on, so set both up.  */
65 
66       value = CGEN_INSN_BASE_VALUE (insn);
67       switch (CGEN_INSN_MASK_BITSIZE (insn))
68 	{
69 	case 8:
70 	  buf[0] = value;
71 	  break;
72 	case 16:
73 	  if (big_p)
74 	    bfd_putb16 ((bfd_vma) value, buf);
75 	  else
76 	    bfd_putl16 ((bfd_vma) value, buf);
77 	  break;
78 	case 32:
79 	  if (big_p)
80 	    bfd_putb32 ((bfd_vma) value, buf);
81 	  else
82 	    bfd_putl32 ((bfd_vma) value, buf);
83 	  break;
84 	default:
85 	  abort ();
86 	}
87 
88       hash = (* cd->dis_hash) (buf, value);
89       hentbuf->next = htable[hash];
90       hentbuf->insn = insn;
91       htable[hash] = hentbuf;
92     }
93 
94   return hentbuf;
95 }
96 
97 /* Subroutine of build_dis_hash_table to add INSNS to the hash table.
98    This function is identical to hash_insn_array except the insns are
99    in a list.  */
100 
101 static CGEN_INSN_LIST *
102 hash_insn_list (cd, insns, htable, hentbuf)
103      CGEN_CPU_DESC cd;
104      const CGEN_INSN_LIST *insns;
105      CGEN_INSN_LIST **htable;
106      CGEN_INSN_LIST *hentbuf;
107 {
108   int big_p = CGEN_CPU_ENDIAN (cd) == CGEN_ENDIAN_BIG;
109   const CGEN_INSN_LIST *ilist;
110 
111   for (ilist = insns; ilist != NULL; ilist = ilist->next, ++ hentbuf)
112     {
113       unsigned int hash;
114       char buf[4];
115       unsigned long value;
116 
117       if (! (* cd->dis_hash_p) (ilist->insn))
118 	continue;
119 
120       /* We don't know whether the target uses the buffer or the base insn
121 	 to hash on, so set both up.  */
122 
123       value = CGEN_INSN_BASE_VALUE (ilist->insn);
124       switch (CGEN_INSN_MASK_BITSIZE (ilist->insn))
125 	{
126 	case 8:
127 	  buf[0] = value;
128 	  break;
129 	case 16:
130 	  if (big_p)
131 	    bfd_putb16 ((bfd_vma) value, buf);
132 	  else
133 	    bfd_putl16 ((bfd_vma) value, buf);
134 	  break;
135 	case 32:
136 	  if (big_p)
137 	    bfd_putb32 ((bfd_vma) value, buf);
138 	  else
139 	    bfd_putl32 ((bfd_vma) value, buf);
140 	  break;
141 	default:
142 	  abort ();
143 	}
144 
145       hash = (* cd->dis_hash) (buf, value);
146       hentbuf->next = htable [hash];
147       hentbuf->insn = ilist->insn;
148       htable [hash] = hentbuf;
149     }
150 
151   return hentbuf;
152 }
153 
154 /* Build the disassembler instruction hash table.  */
155 
156 static void
157 build_dis_hash_table (cd)
158      CGEN_CPU_DESC cd;
159 {
160   int count = cgen_insn_count (cd) + cgen_macro_insn_count (cd);
161   CGEN_INSN_TABLE *insn_table = & cd->insn_table;
162   CGEN_INSN_TABLE *macro_insn_table = & cd->macro_insn_table;
163   unsigned int hash_size = cd->dis_hash_size;
164   CGEN_INSN_LIST *hash_entry_buf;
165   CGEN_INSN_LIST **dis_hash_table;
166   CGEN_INSN_LIST *dis_hash_table_entries;
167 
168   /* The space allocated for the hash table consists of two parts:
169      the hash table and the hash lists.  */
170 
171   dis_hash_table = (CGEN_INSN_LIST **)
172     xmalloc (hash_size * sizeof (CGEN_INSN_LIST *));
173   memset (dis_hash_table, 0, hash_size * sizeof (CGEN_INSN_LIST *));
174   dis_hash_table_entries = hash_entry_buf = (CGEN_INSN_LIST *)
175     xmalloc (count * sizeof (CGEN_INSN_LIST));
176 
177   /* Add compiled in insns.
178      Don't include the first one as it is a reserved entry.  */
179   /* ??? It was the end of all hash chains, and also the special
180      "invalid insn" marker.  May be able to do it differently now.  */
181 
182   hash_entry_buf = hash_insn_array (cd,
183 				    insn_table->init_entries + 1,
184 				    insn_table->num_init_entries - 1,
185 				    insn_table->entry_size,
186 				    dis_hash_table, hash_entry_buf);
187 
188   /* Add compiled in macro-insns.  */
189 
190   hash_entry_buf = hash_insn_array (cd, macro_insn_table->init_entries,
191 				    macro_insn_table->num_init_entries,
192 				    macro_insn_table->entry_size,
193 				    dis_hash_table, hash_entry_buf);
194 
195   /* Add runtime added insns.
196      Later added insns will be prefered over earlier ones.  */
197 
198   hash_entry_buf = hash_insn_list (cd, insn_table->new_entries,
199 				   dis_hash_table, hash_entry_buf);
200 
201   /* Add runtime added macro-insns.  */
202 
203   hash_insn_list (cd, macro_insn_table->new_entries,
204 		  dis_hash_table, hash_entry_buf);
205 
206   cd->dis_hash_table = dis_hash_table;
207   cd->dis_hash_table_entries = dis_hash_table_entries;
208 }
209 
210 /* Return the first entry in the hash list for INSN.  */
211 
212 CGEN_INSN_LIST *
213 cgen_dis_lookup_insn (cd, buf, value)
214      CGEN_CPU_DESC cd;
215      const char * buf;
216      CGEN_INSN_INT value;
217 {
218   unsigned int hash;
219 
220   if (cd->dis_hash_table == NULL)
221     build_dis_hash_table (cd);
222 
223   hash = (* cd->dis_hash) (buf, value);
224 
225   return cd->dis_hash_table[hash];
226 }
227