This file is used to generate @arch@-asm.c.
-Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of the GNU Binutils and GDB, the GNU debugger.
#include <stdio.h>
#include "ansidecl.h"
#include "bfd.h"
+#include "symcat.h"
#include "@arch@-opc.h"
/* ??? The layout of this stuff is still work in progress.
compiled with GCC), or switch to macros, or use something else.
*/
-static const char *parse_insn_normal
+static const char * parse_insn_normal
PARAMS ((const CGEN_INSN *, const char **, CGEN_FIELDS *));
-static void insert_insn_normal
+static const char * insert_insn_normal
PARAMS ((const CGEN_INSN *, CGEN_FIELDS *, cgen_insn_t *));
\f
/* Default insertion routine.
- SHIFT is negative for left shifts, positive for right shifts.
- All bits of VALUE to be inserted must be valid as we don't handle
- signed vs unsigned shifts.
+ ATTRS is a mask of the boolean attributes.
+ LENGTH is the length of VALUE in bits.
+ TOTAL_LENGTH is the total length of the insn (currently 8,16,32).
- ATTRS is a mask of the boolean attributes. We don't need any at the
- moment, but for consistency with extract_normal we have them. */
+ The result is an error message or NULL if success. */
-/* FIXME: This duplicates functionality with bfd's howto table and
+/* ??? This duplicates functionality with bfd's howto table and
bfd_install_relocation. */
-/* FIXME: For architectures where insns can be representable as ints,
- store insn in `field' struct and add registers, etc. while parsing. */
+/* ??? For architectures where insns can be representable as ints,
+ store insn in `field' struct and add registers, etc. while parsing? */
-static CGEN_INLINE void
+static const char *
insert_normal (value, attrs, start, length, shift, total_length, buffer)
long value;
unsigned int attrs;
- int start, length, shift, total_length;
- char *buffer;
+ int start;
+ int length;
+ int shift;
+ int total_length;
+ char * buffer;
{
bfd_vma x;
+ static char buf[100];
+
+ if (shift < 0)
+ value <<= -shift;
+ else
+ value >>= shift;
+
+ /* Ensure VALUE will fit. */
+ if ((attrs & (1 << CGEN_OPERAND_UNSIGNED)) != 0)
+ {
+ unsigned long max = (1 << length) - 1;
+ if ((unsigned long) value > max)
+ {
+ const char *err = "operand out of range (%lu not between 0 and %lu)";
+
+ sprintf (buf, err, value, max);
+ return buf;
+ }
+ }
+ else
+ {
+ long min = - (1 << (length - 1));
+ long max = (1 << (length - 1)) - 1;
+ if (value < min || value > max)
+ {
+ const char *err = "operand out of range (%ld not between %ld and %ld)";
+
+ sprintf (buf, err, value, min, max);
+ return buf;
+ }
+ }
#if 0 /*def CGEN_INT_INSN*/
*buffer |= ((value & ((1 << length) - 1))
switch (total_length)
{
case 8:
- x = *(unsigned char *) buffer;
+ x = * (unsigned char *) buffer;
break;
case 16:
if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG)
abort ();
}
- if (shift < 0)
- value <<= -shift;
- else
- value >>= shift;
-
x |= ((value & ((1 << length) - 1))
<< (total_length - (start + length)));
switch (total_length)
{
case 8:
- *buffer = value;
+ * buffer = value;
break;
case 16:
if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG)
abort ();
}
#endif
+
+ return NULL;
}
\f
/* -- assembler routines inserted here */
static const char *
parse_insn_normal (insn, strp, fields)
- const CGEN_INSN *insn;
- const char **strp;
- CGEN_FIELDS *fields;
+ const CGEN_INSN * insn;
+ const char ** strp;
+ CGEN_FIELDS * fields;
{
- const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
- const char *str = *strp;
- const char *errmsg;
- const char *p;
- const unsigned char *syn;
+ const CGEN_SYNTAX * syntax = CGEN_INSN_SYNTAX (insn);
+ const char * str = *strp;
+ const char * errmsg;
+ const char * p;
+ const unsigned char * syn;
#ifdef CGEN_MNEMONIC_OPERANDS
int past_opcode_p;
#endif
/* For now we assume the mnemonic is first (there are no leading operands).
We can parse it without needing to set up operand parsing. */
p = CGEN_INSN_MNEMONIC (insn);
- while (*p && *p == *str)
- ++p, ++str;
- if (*p || (*str && !isspace (*str)))
+ while (* p && * p == * str)
+ ++ p, ++ str;
+ if (* p || (* str && !isspace (* str)))
return "unrecognized instruction";
CGEN_INIT_PARSE ();
/* We don't check for (*str != '\0') here because we want to parse
any trailing fake arguments in the syntax string. */
syn = CGEN_SYNTAX_STRING (CGEN_INSN_SYNTAX (insn));
+
/* Mnemonics come first for now, ensure valid string. */
- if (! CGEN_SYNTAX_MNEMONIC_P (*syn))
+ if (! CGEN_SYNTAX_MNEMONIC_P (* syn))
abort ();
+
++syn;
- while (*syn != 0)
+
+ while (* syn != 0)
{
/* Non operand chars must match exactly. */
/* FIXME: Need to better handle whitespace. */
- if (CGEN_SYNTAX_CHAR_P (*syn))
+ if (CGEN_SYNTAX_CHAR_P (* syn))
{
- if (*str == CGEN_SYNTAX_CHAR (*syn))
+ if (*str == CGEN_SYNTAX_CHAR (* syn))
{
#ifdef CGEN_MNEMONIC_OPERANDS
- if (*syn == ' ')
+ if (* syn == ' ')
past_opcode_p = 1;
#endif
- ++syn;
- ++str;
+ ++ syn;
+ ++ str;
}
else
{
/* Syntax char didn't match. Can't be this insn. */
- /* FIXME: would like to return "expected char `c'" */
+ /* FIXME: would like to return something like
+ "expected char `c'" */
return "syntax error";
}
continue;
return errmsg;
/* Done with this operand, continue with next one. */
- ++syn;
+ ++ syn;
}
/* If we're at the end of the syntax string, we're done. */
- if (*syn == '\0')
+ if (* syn == '\0')
{
/* FIXME: For the moment we assume a valid `str' can only contain
blanks now. IE: We needn't try again with a longer version of
the insn and it is assumed that longer versions of insns appear
before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */
- while (isspace (*str))
- ++str;
+ while (isspace (* str))
+ ++ str;
- if (*str != '\0')
+ if (* str != '\0')
return "junk at end of line"; /* FIXME: would like to include `str' */
return NULL;
}
/* Default insn builder (insert handler).
- The instruction is recorded in target byte order. */
+ The instruction is recorded in target byte order.
+ The result is an error message or NULL if success. */
+/* FIXME: change buffer to char *? */
-static void
+static const char *
insert_insn_normal (insn, fields, buffer)
- const CGEN_INSN *insn;
- CGEN_FIELDS *fields;
- cgen_insn_t *buffer;
+ const CGEN_INSN * insn;
+ CGEN_FIELDS * fields;
+ cgen_insn_t * buffer;
{
- const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
+ const CGEN_SYNTAX * syntax = CGEN_INSN_SYNTAX (insn);
bfd_vma value;
- const unsigned char *syn;
+ const unsigned char * syn;
CGEN_INIT_INSERT ();
value = CGEN_INSN_VALUE (insn);
switch (min (CGEN_BASE_INSN_BITSIZE, CGEN_FIELDS_BITSIZE (fields)))
{
case 8:
- *buffer = value;
+ * buffer = value;
break;
case 16:
if (CGEN_CURRENT_ENDIAN == CGEN_ENDIAN_BIG)
/* ??? Rather than scanning the syntax string again, we could store
in `fields' a null terminated list of the fields that are present. */
- for (syn = CGEN_SYNTAX_STRING (syntax); *syn != '\0'; ++syn)
+ for (syn = CGEN_SYNTAX_STRING (syntax); * syn != '\0'; ++ syn)
{
- if (CGEN_SYNTAX_CHAR_P (*syn))
+ const char *errmsg;
+
+ if (CGEN_SYNTAX_CHAR_P (* syn))
continue;
- @arch@_cgen_insert_operand (CGEN_SYNTAX_FIELD (*syn), fields, buffer);
+ errmsg = @arch@_cgen_insert_operand (CGEN_SYNTAX_FIELD (*syn), fields,
+ (char *) buffer);
+ if (errmsg)
+ return errmsg;
}
+
+ return NULL;
}
\f
/* Main entry point.
const CGEN_INSN *
@arch@_cgen_assemble_insn (str, fields, buf, errmsg)
- const char *str;
- CGEN_FIELDS *fields;
- cgen_insn_t *buf;
- char **errmsg;
+ const char * str;
+ CGEN_FIELDS * fields;
+ cgen_insn_t * buf;
+ char ** errmsg;
{
- const char *start;
- CGEN_INSN_LIST *ilist;
+ const char * start;
+ CGEN_INSN_LIST * ilist;
/* Skip leading white space. */
- while (isspace (*str))
- ++str;
+ while (isspace (* str))
+ ++ str;
/* The instructions are stored in hashed lists.
Get the first in the list. */
/* FIXME: wip */
CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
- /* ??? The extent to which moving the parse and insert handlers into
- this function (thus removing the function call) will speed things up
- is unclear. The simplicity and flexibility of the current scheme is
- appropriate for now. One could have the best of both worlds with
- inline functions but of course that would only work for gcc. Since
- we're machine generating some code we could do that here too. Maybe
- later. */
- if (! (*CGEN_PARSE_FN (insn)) (insn, &str, fields))
+ if (! CGEN_PARSE_FN (insn) (insn, & str, fields))
{
- (*CGEN_INSERT_FN (insn)) (insn, fields, buf);
+ if (CGEN_INSERT_FN (insn) (insn, fields, buf) != NULL)
+ continue;
/* It is up to the caller to actually output the insn and any
queued relocs. */
return insn;
void
@arch@_cgen_asm_hash_keywords (opvals)
- CGEN_KEYWORD *opvals;
+ CGEN_KEYWORD * opvals;
{
CGEN_KEYWORD_SEARCH search = cgen_keyword_search_init (opvals, NULL);
- const CGEN_KEYWORD_ENTRY *ke;
+ const CGEN_KEYWORD_ENTRY * ke;
- while ((ke = cgen_keyword_search_next (&search)) != NULL)
+ while ((ke = cgen_keyword_search_next (& search)) != NULL)
{
#if 0 /* Unnecessary, should be done in the search routine. */
if (! @arch@_cgen_opval_supported (ke))