1214571Sdim/* Copyright 2007  Free Software Foundation, Inc.
2214571Sdim
3214571Sdim   This file is part of GAS, the GNU Assembler, and GDB, the GNU Debugger.
4214571Sdim
5214571Sdim   This program is free software; you can redistribute it and/or modify
6214571Sdim   it under the terms of the GNU General Public License as published by
7214571Sdim   the Free Software Foundation; either version 2 of the License, or
8214571Sdim   (at your option) any later version.
9214571Sdim
10214571Sdim   This program is distributed in the hope that it will be useful,
11214571Sdim   but WITHOUT ANY WARRANTY; without even the implied warranty of
12214571Sdim   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13214571Sdim   GNU General Public License for more details.
14214571Sdim
15214571Sdim   You should have received a copy of the GNU General Public License
16214571Sdim   along with this program; if not, write to the Free Software
17214571Sdim   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
18214571Sdim
19214571Sdim#include <stdio.h>
20214571Sdim#include <stdlib.h>
21214571Sdim#include <string.h>
22214571Sdim#include <errno.h>
23214571Sdim#include "getopt.h"
24214571Sdim#include "libiberty.h"
25214571Sdim#include "safe-ctype.h"
26214571Sdim
27214571Sdim#include "i386-opc.h"
28214571Sdim
29214571Sdim#include <libintl.h>
30214571Sdim#define _(String) gettext (String)
31214571Sdim
32214571Sdimstatic const char *program_name = NULL;
33214571Sdimstatic int debug = 0;
34214571Sdim
35214571Sdimstatic void
36214571Sdimfail (const char *message, ...)
37214571Sdim{
38214571Sdim  va_list args;
39214571Sdim
40214571Sdim  va_start (args, message);
41214571Sdim  fprintf (stderr, _("%s: Error: "), program_name);
42214571Sdim  vfprintf (stderr, message, args);
43214571Sdim  va_end (args);
44214571Sdim  xexit (1);
45214571Sdim}
46214571Sdim
47214571Sdim/* Remove leading white spaces.  */
48214571Sdim
49214571Sdimstatic char *
50214571Sdimremove_leading_whitespaces (char *str)
51214571Sdim{
52214571Sdim  while (ISSPACE (*str))
53214571Sdim    str++;
54214571Sdim  return str;
55214571Sdim}
56214571Sdim
57214571Sdim/* Remove trailing white spaces.  */
58214571Sdim
59214571Sdimstatic void
60214571Sdimremove_trailing_whitespaces (char *str)
61214571Sdim{
62214571Sdim  size_t last = strlen (str);
63214571Sdim
64214571Sdim  if (last == 0)
65214571Sdim    return;
66214571Sdim
67214571Sdim  do
68214571Sdim    {
69214571Sdim      last--;
70214571Sdim      if (ISSPACE (str [last]))
71214571Sdim	str[last] = '\0';
72214571Sdim      else
73214571Sdim	break;
74214571Sdim    }
75214571Sdim  while (last != 0);
76214571Sdim}
77214571Sdim
78214571Sdim/* Find next field separated by '.' and terminate it. Return a
79214571Sdim   pointer to the one after it.  */
80214571Sdim
81214571Sdimstatic char *
82214571Sdimnext_field (char *str, char **next)
83214571Sdim{
84214571Sdim  char *p;
85214571Sdim
86214571Sdim  p = remove_leading_whitespaces (str);
87214571Sdim  for (str = p; *str != ',' && *str != '\0'; str++);
88214571Sdim
89214571Sdim  *str = '\0';
90214571Sdim  remove_trailing_whitespaces (p);
91214571Sdim
92214571Sdim  *next = str + 1;
93214571Sdim
94214571Sdim  return p;
95214571Sdim}
96214571Sdim
97214571Sdimstatic void
98214571Sdimprocess_i386_opcodes (void)
99214571Sdim{
100214571Sdim  FILE *fp = fopen ("i386-opc.tbl", "r");
101214571Sdim  char buf[2048];
102214571Sdim  unsigned int i;
103214571Sdim  char *str, *p, *last;
104214571Sdim  char *name, *operands, *base_opcode, *extension_opcode;
105214571Sdim  char *cpu_flags, *opcode_modifier, *operand_types [MAX_OPERANDS];
106214571Sdim
107214571Sdim  if (fp == NULL)
108214571Sdim    fail (_("can't find i386-opc.tbl for reading\n"));
109214571Sdim
110214571Sdim  printf ("\n/* i386 opcode table.  */\n\n");
111214571Sdim  printf ("const template i386_optab[] =\n{\n");
112214571Sdim
113214571Sdim  while (!feof (fp))
114214571Sdim    {
115214571Sdim      if (fgets (buf, sizeof (buf), fp) == NULL)
116214571Sdim	break;
117214571Sdim
118214571Sdim      p = remove_leading_whitespaces (buf);
119214571Sdim
120214571Sdim      /* Skip comments.  */
121214571Sdim      str = strstr (p, "//");
122214571Sdim      if (str != NULL)
123214571Sdim	str[0] = '\0';
124214571Sdim
125214571Sdim      /* Remove trailing white spaces.  */
126214571Sdim      remove_trailing_whitespaces (p);
127214571Sdim
128214571Sdim      switch (p[0])
129214571Sdim	{
130214571Sdim	case '#':
131214571Sdim	  printf ("%s\n", p);
132214571Sdim	case '\0':
133214571Sdim	  continue;
134214571Sdim	  break;
135214571Sdim	default:
136214571Sdim	  break;
137214571Sdim	}
138214571Sdim
139214571Sdim      last = p + strlen (p);
140214571Sdim
141214571Sdim      /* Find name.  */
142214571Sdim      name = next_field (p, &str);
143214571Sdim
144214571Sdim      if (str >= last)
145214571Sdim	abort ();
146214571Sdim
147214571Sdim      /* Find number of operands.  */
148214571Sdim      operands = next_field (str, &str);
149214571Sdim
150214571Sdim      if (str >= last)
151214571Sdim	abort ();
152214571Sdim
153214571Sdim      /* Find base_opcode.  */
154214571Sdim      base_opcode = next_field (str, &str);
155214571Sdim
156214571Sdim      if (str >= last)
157214571Sdim	abort ();
158214571Sdim
159214571Sdim      /* Find extension_opcode.  */
160214571Sdim      extension_opcode = next_field (str, &str);
161214571Sdim
162214571Sdim      if (str >= last)
163214571Sdim	abort ();
164214571Sdim
165214571Sdim      /* Find cpu_flags.  */
166214571Sdim      cpu_flags = next_field (str, &str);
167214571Sdim
168214571Sdim      if (str >= last)
169214571Sdim	abort ();
170214571Sdim
171214571Sdim      /* Find opcode_modifier.  */
172214571Sdim      opcode_modifier = next_field (str, &str);
173214571Sdim
174214571Sdim      if (str >= last)
175214571Sdim	abort ();
176214571Sdim
177214571Sdim      /* Remove the first {.  */
178214571Sdim      str = remove_leading_whitespaces (str);
179214571Sdim      if (*str != '{')
180214571Sdim	abort ();
181214571Sdim      str = remove_leading_whitespaces (str + 1);
182214571Sdim
183214571Sdim      i = strlen (str);
184214571Sdim
185214571Sdim      /* There are at least "X}".  */
186214571Sdim      if (i < 2)
187214571Sdim	abort ();
188214571Sdim
189214571Sdim      /* Remove trailing white spaces and }. */
190214571Sdim      do
191214571Sdim	{
192214571Sdim	  i--;
193214571Sdim	  if (ISSPACE (str[i]) || str[i] == '}')
194214571Sdim	    str[i] = '\0';
195214571Sdim	  else
196214571Sdim	    break;
197214571Sdim	}
198214571Sdim      while (i != 0);
199214571Sdim
200214571Sdim      last = str + i;
201214571Sdim
202214571Sdim      /* Find operand_types.  */
203214571Sdim      for (i = 0; i < ARRAY_SIZE (operand_types); i++)
204214571Sdim	{
205214571Sdim	  if (str >= last)
206214571Sdim	    {
207214571Sdim	      operand_types [i] = NULL;
208214571Sdim	      break;
209214571Sdim	    }
210214571Sdim
211214571Sdim	  operand_types [i] = next_field (str, &str);
212214571Sdim	  if (*operand_types[i] == '0')
213214571Sdim	    {
214214571Sdim	      if (i != 0)
215214571Sdim		operand_types[i] = NULL;
216214571Sdim	      break;
217214571Sdim	    }
218214571Sdim	}
219214571Sdim
220214571Sdim      printf ("  { \"%s\", %s, %s, %s, %s,\n",
221214571Sdim	      name, operands, base_opcode, extension_opcode,
222214571Sdim	      cpu_flags);
223214571Sdim
224214571Sdim      printf ("    %s,\n", opcode_modifier);
225214571Sdim
226214571Sdim      printf ("    { ");
227214571Sdim
228214571Sdim      for (i = 0; i < ARRAY_SIZE (operand_types); i++)
229214571Sdim	{
230214571Sdim	  if (operand_types[i] == NULL
231214571Sdim	      || *operand_types[i] == '0')
232214571Sdim	    {
233214571Sdim	      if (i == 0)
234214571Sdim		printf ("0");
235214571Sdim	      break;
236214571Sdim	    }
237214571Sdim
238214571Sdim	  if (i != 0)
239214571Sdim	    printf (",\n      ");
240214571Sdim
241214571Sdim	  printf ("%s", operand_types[i]);
242214571Sdim	}
243214571Sdim      printf (" } },\n");
244214571Sdim    }
245214571Sdim
246214571Sdim  printf ("  { NULL, 0, 0, 0, 0, 0, { 0 } }\n");
247214571Sdim  printf ("};\n");
248214571Sdim}
249214571Sdim
250214571Sdimstatic void
251214571Sdimprocess_i386_registers (void)
252214571Sdim{
253214571Sdim  FILE *fp = fopen ("i386-reg.tbl", "r");
254214571Sdim  char buf[2048];
255214571Sdim  char *str, *p, *last;
256214571Sdim  char *reg_name, *reg_type, *reg_flags, *reg_num;
257214571Sdim
258214571Sdim  if (fp == NULL)
259214571Sdim    fail (_("can't find i386-reg.tbl for reading\n"));
260214571Sdim
261214571Sdim  printf ("\n/* i386 register table.  */\n\n");
262214571Sdim  printf ("const reg_entry i386_regtab[] =\n{\n");
263214571Sdim
264214571Sdim  while (!feof (fp))
265214571Sdim    {
266214571Sdim      if (fgets (buf, sizeof (buf), fp) == NULL)
267214571Sdim	break;
268214571Sdim
269214571Sdim      p = remove_leading_whitespaces (buf);
270214571Sdim
271214571Sdim      /* Skip comments.  */
272214571Sdim      str = strstr (p, "//");
273214571Sdim      if (str != NULL)
274214571Sdim	str[0] = '\0';
275214571Sdim
276214571Sdim      /* Remove trailing white spaces.  */
277214571Sdim      remove_trailing_whitespaces (p);
278214571Sdim
279214571Sdim      switch (p[0])
280214571Sdim	{
281214571Sdim	case '#':
282214571Sdim	  printf ("%s\n", p);
283214571Sdim	case '\0':
284214571Sdim	  continue;
285214571Sdim	  break;
286214571Sdim	default:
287214571Sdim	  break;
288214571Sdim	}
289214571Sdim
290214571Sdim      last = p + strlen (p);
291214571Sdim
292214571Sdim      /* Find reg_name.  */
293214571Sdim      reg_name = next_field (p, &str);
294214571Sdim
295214571Sdim      if (str >= last)
296214571Sdim	abort ();
297214571Sdim
298214571Sdim      /* Find reg_type.  */
299214571Sdim      reg_type = next_field (str, &str);
300214571Sdim
301214571Sdim      if (str >= last)
302214571Sdim	abort ();
303214571Sdim
304214571Sdim      /* Find reg_flags.  */
305214571Sdim      reg_flags = next_field (str, &str);
306214571Sdim
307214571Sdim      if (str >= last)
308214571Sdim	abort ();
309214571Sdim
310214571Sdim      /* Find reg_num.  */
311214571Sdim      reg_num = next_field (str, &str);
312214571Sdim
313214571Sdim      printf ("  { \"%s\", %s, %s, %s },\n",
314214571Sdim	      reg_name, reg_type, reg_flags, reg_num);
315214571Sdim    }
316214571Sdim
317214571Sdim  printf ("};\n");
318214571Sdim
319214571Sdim  printf ("\nconst unsigned int i386_regtab_size = ARRAY_SIZE (i386_regtab);\n");
320214571Sdim}
321214571Sdim
322214571Sdim/* Program options.  */
323214571Sdim#define OPTION_SRCDIR	200
324214571Sdim
325214571Sdimstruct option long_options[] =
326214571Sdim{
327214571Sdim  {"srcdir",  required_argument, NULL, OPTION_SRCDIR},
328214571Sdim  {"debug",   no_argument,       NULL, 'd'},
329214571Sdim  {"version", no_argument,       NULL, 'V'},
330214571Sdim  {"help",    no_argument,       NULL, 'h'},
331214571Sdim  {0,         no_argument,       NULL, 0}
332214571Sdim};
333214571Sdim
334214571Sdimstatic void
335214571Sdimprint_version (void)
336214571Sdim{
337214571Sdim  printf ("%s: version 1.0\n", program_name);
338214571Sdim  xexit (0);
339214571Sdim}
340214571Sdim
341214571Sdimstatic void
342214571Sdimusage (FILE * stream, int status)
343214571Sdim{
344214571Sdim  fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--srcdir=dirname] [--help]\n",
345214571Sdim	   program_name);
346214571Sdim  xexit (status);
347214571Sdim}
348214571Sdim
349214571Sdimint
350214571Sdimmain (int argc, char **argv)
351214571Sdim{
352214571Sdim  extern int chdir (char *);
353214571Sdim  char *srcdir = NULL;
354214571Sdim  int c;
355214571Sdim
356214571Sdim  program_name = *argv;
357214571Sdim  xmalloc_set_program_name (program_name);
358214571Sdim
359214571Sdim  while ((c = getopt_long (argc, argv, "vVdh", long_options, 0)) != EOF)
360214571Sdim    switch (c)
361214571Sdim      {
362214571Sdim      case OPTION_SRCDIR:
363214571Sdim	srcdir = optarg;
364214571Sdim	break;
365214571Sdim      case 'V':
366214571Sdim      case 'v':
367214571Sdim	print_version ();
368214571Sdim	break;
369214571Sdim      case 'd':
370214571Sdim	debug = 1;
371214571Sdim	break;
372214571Sdim      case 'h':
373214571Sdim      case '?':
374214571Sdim	usage (stderr, 0);
375214571Sdim      default:
376214571Sdim      case 0:
377214571Sdim	break;
378214571Sdim      }
379214571Sdim
380214571Sdim  if (optind != argc)
381214571Sdim    usage (stdout, 1);
382214571Sdim
383214571Sdim  if (srcdir != NULL)
384214571Sdim    if (chdir (srcdir) != 0)
385214571Sdim      fail (_("unable to change directory to \"%s\", errno = %s\n"),
386214571Sdim	    srcdir, strerror (errno));
387214571Sdim
388214571Sdim  printf ("/* This file is automatically generated by i386-gen.  Do not edit!  */\n");
389214571Sdim
390214571Sdim  process_i386_opcodes ();
391214571Sdim  process_i386_registers ();
392214571Sdim
393214571Sdim  exit (0);
394214571Sdim}
395