1/* Copyright 2007  Free Software Foundation, Inc.
2
3   This file is part of GAS, the GNU Assembler, and GDB, the GNU Debugger.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <errno.h>
23#include "getopt.h"
24#include "libiberty.h"
25#include "safe-ctype.h"
26
27#include "i386-opc.h"
28
29#include <libintl.h>
30#define _(String) gettext (String)
31
32static const char *program_name = NULL;
33static int debug = 0;
34
35static void
36fail (const char *message, ...)
37{
38  va_list args;
39
40  va_start (args, message);
41  fprintf (stderr, _("%s: Error: "), program_name);
42  vfprintf (stderr, message, args);
43  va_end (args);
44  xexit (1);
45}
46
47/* Remove leading white spaces.  */
48
49static char *
50remove_leading_whitespaces (char *str)
51{
52  while (ISSPACE (*str))
53    str++;
54  return str;
55}
56
57/* Remove trailing white spaces.  */
58
59static void
60remove_trailing_whitespaces (char *str)
61{
62  size_t last = strlen (str);
63
64  if (last == 0)
65    return;
66
67  do
68    {
69      last--;
70      if (ISSPACE (str [last]))
71	str[last] = '\0';
72      else
73	break;
74    }
75  while (last != 0);
76}
77
78/* Find next field separated by '.' and terminate it. Return a
79   pointer to the one after it.  */
80
81static char *
82next_field (char *str, char **next)
83{
84  char *p;
85
86  p = remove_leading_whitespaces (str);
87  for (str = p; *str != ',' && *str != '\0'; str++);
88
89  *str = '\0';
90  remove_trailing_whitespaces (p);
91
92  *next = str + 1;
93
94  return p;
95}
96
97static void
98process_i386_opcodes (void)
99{
100  FILE *fp = fopen ("i386-opc.tbl", "r");
101  char buf[2048];
102  unsigned int i;
103  char *str, *p, *last;
104  char *name, *operands, *base_opcode, *extension_opcode;
105  char *cpu_flags, *opcode_modifier, *operand_types [MAX_OPERANDS];
106
107  if (fp == NULL)
108    fail (_("can't find i386-opc.tbl for reading\n"));
109
110  printf ("\n/* i386 opcode table.  */\n\n");
111  printf ("const template i386_optab[] =\n{\n");
112
113  while (!feof (fp))
114    {
115      if (fgets (buf, sizeof (buf), fp) == NULL)
116	break;
117
118      p = remove_leading_whitespaces (buf);
119
120      /* Skip comments.  */
121      str = strstr (p, "//");
122      if (str != NULL)
123	str[0] = '\0';
124
125      /* Remove trailing white spaces.  */
126      remove_trailing_whitespaces (p);
127
128      switch (p[0])
129	{
130	case '#':
131	  printf ("%s\n", p);
132	case '\0':
133	  continue;
134	  break;
135	default:
136	  break;
137	}
138
139      last = p + strlen (p);
140
141      /* Find name.  */
142      name = next_field (p, &str);
143
144      if (str >= last)
145	abort ();
146
147      /* Find number of operands.  */
148      operands = next_field (str, &str);
149
150      if (str >= last)
151	abort ();
152
153      /* Find base_opcode.  */
154      base_opcode = next_field (str, &str);
155
156      if (str >= last)
157	abort ();
158
159      /* Find extension_opcode.  */
160      extension_opcode = next_field (str, &str);
161
162      if (str >= last)
163	abort ();
164
165      /* Find cpu_flags.  */
166      cpu_flags = next_field (str, &str);
167
168      if (str >= last)
169	abort ();
170
171      /* Find opcode_modifier.  */
172      opcode_modifier = next_field (str, &str);
173
174      if (str >= last)
175	abort ();
176
177      /* Remove the first {.  */
178      str = remove_leading_whitespaces (str);
179      if (*str != '{')
180	abort ();
181      str = remove_leading_whitespaces (str + 1);
182
183      i = strlen (str);
184
185      /* There are at least "X}".  */
186      if (i < 2)
187	abort ();
188
189      /* Remove trailing white spaces and }. */
190      do
191	{
192	  i--;
193	  if (ISSPACE (str[i]) || str[i] == '}')
194	    str[i] = '\0';
195	  else
196	    break;
197	}
198      while (i != 0);
199
200      last = str + i;
201
202      /* Find operand_types.  */
203      for (i = 0; i < ARRAY_SIZE (operand_types); i++)
204	{
205	  if (str >= last)
206	    {
207	      operand_types [i] = NULL;
208	      break;
209	    }
210
211	  operand_types [i] = next_field (str, &str);
212	  if (*operand_types[i] == '0')
213	    {
214	      if (i != 0)
215		operand_types[i] = NULL;
216	      break;
217	    }
218	}
219
220      printf ("  { \"%s\", %s, %s, %s, %s,\n",
221	      name, operands, base_opcode, extension_opcode,
222	      cpu_flags);
223
224      printf ("    %s,\n", opcode_modifier);
225
226      printf ("    { ");
227
228      for (i = 0; i < ARRAY_SIZE (operand_types); i++)
229	{
230	  if (operand_types[i] == NULL
231	      || *operand_types[i] == '0')
232	    {
233	      if (i == 0)
234		printf ("0");
235	      break;
236	    }
237
238	  if (i != 0)
239	    printf (",\n      ");
240
241	  printf ("%s", operand_types[i]);
242	}
243      printf (" } },\n");
244    }
245
246  printf ("  { NULL, 0, 0, 0, 0, 0, { 0 } }\n");
247  printf ("};\n");
248}
249
250static void
251process_i386_registers (void)
252{
253  FILE *fp = fopen ("i386-reg.tbl", "r");
254  char buf[2048];
255  char *str, *p, *last;
256  char *reg_name, *reg_type, *reg_flags, *reg_num;
257
258  if (fp == NULL)
259    fail (_("can't find i386-reg.tbl for reading\n"));
260
261  printf ("\n/* i386 register table.  */\n\n");
262  printf ("const reg_entry i386_regtab[] =\n{\n");
263
264  while (!feof (fp))
265    {
266      if (fgets (buf, sizeof (buf), fp) == NULL)
267	break;
268
269      p = remove_leading_whitespaces (buf);
270
271      /* Skip comments.  */
272      str = strstr (p, "//");
273      if (str != NULL)
274	str[0] = '\0';
275
276      /* Remove trailing white spaces.  */
277      remove_trailing_whitespaces (p);
278
279      switch (p[0])
280	{
281	case '#':
282	  printf ("%s\n", p);
283	case '\0':
284	  continue;
285	  break;
286	default:
287	  break;
288	}
289
290      last = p + strlen (p);
291
292      /* Find reg_name.  */
293      reg_name = next_field (p, &str);
294
295      if (str >= last)
296	abort ();
297
298      /* Find reg_type.  */
299      reg_type = next_field (str, &str);
300
301      if (str >= last)
302	abort ();
303
304      /* Find reg_flags.  */
305      reg_flags = next_field (str, &str);
306
307      if (str >= last)
308	abort ();
309
310      /* Find reg_num.  */
311      reg_num = next_field (str, &str);
312
313      printf ("  { \"%s\", %s, %s, %s },\n",
314	      reg_name, reg_type, reg_flags, reg_num);
315    }
316
317  printf ("};\n");
318
319  printf ("\nconst unsigned int i386_regtab_size = ARRAY_SIZE (i386_regtab);\n");
320}
321
322/* Program options.  */
323#define OPTION_SRCDIR	200
324
325struct option long_options[] =
326{
327  {"srcdir",  required_argument, NULL, OPTION_SRCDIR},
328  {"debug",   no_argument,       NULL, 'd'},
329  {"version", no_argument,       NULL, 'V'},
330  {"help",    no_argument,       NULL, 'h'},
331  {0,         no_argument,       NULL, 0}
332};
333
334static void
335print_version (void)
336{
337  printf ("%s: version 1.0\n", program_name);
338  xexit (0);
339}
340
341static void
342usage (FILE * stream, int status)
343{
344  fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--srcdir=dirname] [--help]\n",
345	   program_name);
346  xexit (status);
347}
348
349int
350main (int argc, char **argv)
351{
352  extern int chdir (char *);
353  char *srcdir = NULL;
354  int c;
355
356  program_name = *argv;
357  xmalloc_set_program_name (program_name);
358
359  while ((c = getopt_long (argc, argv, "vVdh", long_options, 0)) != EOF)
360    switch (c)
361      {
362      case OPTION_SRCDIR:
363	srcdir = optarg;
364	break;
365      case 'V':
366      case 'v':
367	print_version ();
368	break;
369      case 'd':
370	debug = 1;
371	break;
372      case 'h':
373      case '?':
374	usage (stderr, 0);
375      default:
376      case 0:
377	break;
378      }
379
380  if (optind != argc)
381    usage (stdout, 1);
382
383  if (srcdir != NULL)
384    if (chdir (srcdir) != 0)
385      fail (_("unable to change directory to \"%s\", errno = %s\n"),
386	    srcdir, strerror (errno));
387
388  printf ("/* This file is automatically generated by i386-gen.  Do not edit!  */\n");
389
390  process_i386_opcodes ();
391  process_i386_registers ();
392
393  exit (0);
394}
395