diff options
Diffstat (limited to 'opcodes/i386-gen.c')
-rw-r--r-- | opcodes/i386-gen.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/opcodes/i386-gen.c b/opcodes/i386-gen.c new file mode 100644 index 000000000000..80d414f4a80a --- /dev/null +++ b/opcodes/i386-gen.c @@ -0,0 +1,394 @@ +/* Copyright 2007 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler, and GDB, the GNU Debugger. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "getopt.h" +#include "libiberty.h" +#include "safe-ctype.h" + +#include "i386-opc.h" + +#include <libintl.h> +#define _(String) gettext (String) + +static const char *program_name = NULL; +static int debug = 0; + +static void +fail (const char *message, ...) +{ + va_list args; + + va_start (args, message); + fprintf (stderr, _("%s: Error: "), program_name); + vfprintf (stderr, message, args); + va_end (args); + xexit (1); +} + +/* Remove leading white spaces. */ + +static char * +remove_leading_whitespaces (char *str) +{ + while (ISSPACE (*str)) + str++; + return str; +} + +/* Remove trailing white spaces. */ + +static void +remove_trailing_whitespaces (char *str) +{ + size_t last = strlen (str); + + if (last == 0) + return; + + do + { + last--; + if (ISSPACE (str [last])) + str[last] = '\0'; + else + break; + } + while (last != 0); +} + +/* Find next field separated by '.' and terminate it. Return a + pointer to the one after it. */ + +static char * +next_field (char *str, char **next) +{ + char *p; + + p = remove_leading_whitespaces (str); + for (str = p; *str != ',' && *str != '\0'; str++); + + *str = '\0'; + remove_trailing_whitespaces (p); + + *next = str + 1; + + return p; +} + +static void +process_i386_opcodes (void) +{ + FILE *fp = fopen ("i386-opc.tbl", "r"); + char buf[2048]; + unsigned int i; + char *str, *p, *last; + char *name, *operands, *base_opcode, *extension_opcode; + char *cpu_flags, *opcode_modifier, *operand_types [MAX_OPERANDS]; + + if (fp == NULL) + fail (_("can't find i386-opc.tbl for reading\n")); + + printf ("\n/* i386 opcode table. */\n\n"); + printf ("const template i386_optab[] =\n{\n"); + + while (!feof (fp)) + { + if (fgets (buf, sizeof (buf), fp) == NULL) + break; + + p = remove_leading_whitespaces (buf); + + /* Skip comments. */ + str = strstr (p, "//"); + if (str != NULL) + str[0] = '\0'; + + /* Remove trailing white spaces. */ + remove_trailing_whitespaces (p); + + switch (p[0]) + { + case '#': + printf ("%s\n", p); + case '\0': + continue; + break; + default: + break; + } + + last = p + strlen (p); + + /* Find name. */ + name = next_field (p, &str); + + if (str >= last) + abort (); + + /* Find number of operands. */ + operands = next_field (str, &str); + + if (str >= last) + abort (); + + /* Find base_opcode. */ + base_opcode = next_field (str, &str); + + if (str >= last) + abort (); + + /* Find extension_opcode. */ + extension_opcode = next_field (str, &str); + + if (str >= last) + abort (); + + /* Find cpu_flags. */ + cpu_flags = next_field (str, &str); + + if (str >= last) + abort (); + + /* Find opcode_modifier. */ + opcode_modifier = next_field (str, &str); + + if (str >= last) + abort (); + + /* Remove the first {. */ + str = remove_leading_whitespaces (str); + if (*str != '{') + abort (); + str = remove_leading_whitespaces (str + 1); + + i = strlen (str); + + /* There are at least "X}". */ + if (i < 2) + abort (); + + /* Remove trailing white spaces and }. */ + do + { + i--; + if (ISSPACE (str[i]) || str[i] == '}') + str[i] = '\0'; + else + break; + } + while (i != 0); + + last = str + i; + + /* Find operand_types. */ + for (i = 0; i < ARRAY_SIZE (operand_types); i++) + { + if (str >= last) + { + operand_types [i] = NULL; + break; + } + + operand_types [i] = next_field (str, &str); + if (*operand_types[i] == '0') + { + if (i != 0) + operand_types[i] = NULL; + break; + } + } + + printf (" { \"%s\", %s, %s, %s, %s,\n", + name, operands, base_opcode, extension_opcode, + cpu_flags); + + printf (" %s,\n", opcode_modifier); + + printf (" { "); + + for (i = 0; i < ARRAY_SIZE (operand_types); i++) + { + if (operand_types[i] == NULL + || *operand_types[i] == '0') + { + if (i == 0) + printf ("0"); + break; + } + + if (i != 0) + printf (",\n "); + + printf ("%s", operand_types[i]); + } + printf (" } },\n"); + } + + printf (" { NULL, 0, 0, 0, 0, 0, { 0 } }\n"); + printf ("};\n"); +} + +static void +process_i386_registers (void) +{ + FILE *fp = fopen ("i386-reg.tbl", "r"); + char buf[2048]; + char *str, *p, *last; + char *reg_name, *reg_type, *reg_flags, *reg_num; + + if (fp == NULL) + fail (_("can't find i386-reg.tbl for reading\n")); + + printf ("\n/* i386 register table. */\n\n"); + printf ("const reg_entry i386_regtab[] =\n{\n"); + + while (!feof (fp)) + { + if (fgets (buf, sizeof (buf), fp) == NULL) + break; + + p = remove_leading_whitespaces (buf); + + /* Skip comments. */ + str = strstr (p, "//"); + if (str != NULL) + str[0] = '\0'; + + /* Remove trailing white spaces. */ + remove_trailing_whitespaces (p); + + switch (p[0]) + { + case '#': + printf ("%s\n", p); + case '\0': + continue; + break; + default: + break; + } + + last = p + strlen (p); + + /* Find reg_name. */ + reg_name = next_field (p, &str); + + if (str >= last) + abort (); + + /* Find reg_type. */ + reg_type = next_field (str, &str); + + if (str >= last) + abort (); + + /* Find reg_flags. */ + reg_flags = next_field (str, &str); + + if (str >= last) + abort (); + + /* Find reg_num. */ + reg_num = next_field (str, &str); + + printf (" { \"%s\", %s, %s, %s },\n", + reg_name, reg_type, reg_flags, reg_num); + } + + printf ("};\n"); + + printf ("\nconst unsigned int i386_regtab_size = ARRAY_SIZE (i386_regtab);\n"); +} + +/* Program options. */ +#define OPTION_SRCDIR 200 + +struct option long_options[] = +{ + {"srcdir", required_argument, NULL, OPTION_SRCDIR}, + {"debug", no_argument, NULL, 'd'}, + {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {0, no_argument, NULL, 0} +}; + +static void +print_version (void) +{ + printf ("%s: version 1.0\n", program_name); + xexit (0); +} + +static void +usage (FILE * stream, int status) +{ + fprintf (stream, "Usage: %s [-V | --version] [-d | --debug] [--srcdir=dirname] [--help]\n", + program_name); + xexit (status); +} + +int +main (int argc, char **argv) +{ + extern int chdir (char *); + char *srcdir = NULL; + int c; + + program_name = *argv; + xmalloc_set_program_name (program_name); + + while ((c = getopt_long (argc, argv, "vVdh", long_options, 0)) != EOF) + switch (c) + { + case OPTION_SRCDIR: + srcdir = optarg; + break; + case 'V': + case 'v': + print_version (); + break; + case 'd': + debug = 1; + break; + case 'h': + case '?': + usage (stderr, 0); + default: + case 0: + break; + } + + if (optind != argc) + usage (stdout, 1); + + if (srcdir != NULL) + if (chdir (srcdir) != 0) + fail (_("unable to change directory to \"%s\", errno = %s\n"), + srcdir, strerror (errno)); + + printf ("/* This file is automatically generated by i386-gen. Do not edit! */\n"); + + process_i386_opcodes (); + process_i386_registers (); + + exit (0); +} |