--- /dev/null
+/* tc-mn10200.c -- Assembler code for the Matsushita 10200
+
+ Copyright (C) 1996 Free Software Foundation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS 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, or (at your option)
+ any later version.
+
+ GAS 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 GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "as.h"
+#include "subsegs.h"
+#include "opcode/mn10200.h"
+\f
+/* Structure to hold information about predefined registers. */
+struct reg_name
+{
+ const char *name;
+ int value;
+};
+
+/* Generic assembler global variables which must be defined by all targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+\f
+
+/* local functions */
+static void mn10200_insert_operand PARAMS ((unsigned long *, unsigned long *,
+ const struct mn10200_operand *,
+ offsetT, char *, unsigned,
+ unsigned));
+static unsigned long check_operand PARAMS ((unsigned long,
+ const struct mn10200_operand *,
+ offsetT));
+static int reg_name_search PARAMS ((const struct reg_name *, int, const char *));
+static boolean data_register_name PARAMS ((expressionS *expressionP));
+static boolean address_register_name PARAMS ((expressionS *expressionP));
+static boolean other_register_name PARAMS ((expressionS *expressionP));
+
+
+/* fixups */
+#define MAX_INSN_FIXUPS (5)
+struct mn10200_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+struct mn10200_fixup fixups[MAX_INSN_FIXUPS];
+static int fc;
+\f
+const char *md_shortopts = "";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof(md_longopts);
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *mn10200_hash;
+
+/* This table is sorted. Suitable for searching by a binary search. */
+static const struct reg_name data_registers[] =
+{
+ { "d0", 0 },
+ { "d1", 1 },
+ { "d2", 2 },
+ { "d3", 3 },
+};
+#define DATA_REG_NAME_CNT (sizeof(data_registers) / sizeof(struct reg_name))
+
+static const struct reg_name address_registers[] =
+{
+ { "a0", 0 },
+ { "a1", 1 },
+ { "a2", 2 },
+ { "a3", 3 },
+};
+#define ADDRESS_REG_NAME_CNT (sizeof(address_registers) / sizeof(struct reg_name))
+
+static const struct reg_name other_registers[] =
+{
+ { "mdr", 0 },
+ { "psw", 0 },
+};
+#define OTHER_REG_NAME_CNT (sizeof(other_registers) / sizeof(struct reg_name))
+
+/* reg_name_search does a binary search of the given register table
+ to see if "name" is a valid regiter name. Returns the register
+ number from the array on success, or -1 on failure. */
+
+static int
+reg_name_search (regs, regcount, name)
+ const struct reg_name *regs;
+ int regcount;
+ const char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+ return -1;
+}
+
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+data_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (data_registers, DATA_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+address_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (address_registers, ADDRESS_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+/* Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in
+ * its original state.
+ */
+static boolean
+other_register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (other_registers, OTHER_REG_NAME_CNT, name);
+
+ /* look to see if it's in the register table */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* make the rest nice */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ *input_line_pointer = c; /* put back the delimiting char */
+ return true;
+ }
+ else
+ {
+ /* reset the line as if we had not done anything */
+ *input_line_pointer = c; /* put back the delimiting char */
+ input_line_pointer = start; /* reset input_line pointer */
+ return false;
+ }
+}
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+ fprintf(stream, "MN10200 options:\n\
+none yet\n");
+}
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return 0;
+}
+
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+char *
+md_atof (type, litp, sizep)
+ int type;
+ char *litp;
+ int *sizep;
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ default:
+ *sizep = 0;
+ return "bad call to md_atof";
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizep = prec * 2;
+
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litp, (valueT) words[i], 2);
+ litp += 2;
+ }
+
+ return NULL;
+}
+
+
+void
+md_convert_frag (abfd, sec, fragP)
+ bfd *abfd;
+ asection *sec;
+ fragS *fragP;
+{
+ /* printf ("call to md_convert_frag \n"); */
+ abort ();
+}
+
+valueT
+md_section_align (seg, addr)
+ asection *seg;
+ valueT addr;
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin ()
+{
+ char *prev_name = "";
+ register const struct mn10200_opcode *op;
+
+ mn10200_hash = hash_new();
+
+ /* Insert unique names into hash table. The MN10200 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ op = mn10200_opcodes;
+ while (op->name)
+ {
+ if (strcmp (prev_name, op->name))
+ {
+ prev_name = (char *) op->name;
+ hash_insert (mn10200_hash, op->name, (char *) op);
+ }
+ op++;
+ }
+
+ /* This is both a simplification (we don't have to write md_apply_fix)
+ and support for future optimizations (branch shortening and similar
+ stuff in the linker. */
+ linkrelax = 1;
+}
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *s;
+ struct mn10200_opcode *opcode;
+ struct mn10200_opcode *next_opcode;
+ const unsigned char *opindex_ptr;
+ int next_opindex;
+ unsigned long insn, extension, size = 0;
+ char *f;
+ int i;
+ int match;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! isspace (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* find the first opcode with the proper name */
+ opcode = (struct mn10200_opcode *)hash_find (mn10200_hash, str);
+ if (opcode == NULL)
+ {
+ as_bad ("Unrecognized opcode: `%s'", str);
+ return;
+ }
+
+ str = s;
+ while (isspace (*str))
+ ++str;
+
+ input_line_pointer = str;
+
+ for(;;)
+ {
+ const char *errmsg = NULL;
+ int op_idx;
+ char *hold;
+ int extra_shift = 0;
+
+ fc = 0;
+ match = 0;
+ next_opindex = 0;
+ insn = opcode->opcode;
+ extension = 0;
+ for (op_idx = 1, opindex_ptr = opcode->operands;
+ *opindex_ptr != 0;
+ opindex_ptr++, op_idx++)
+ {
+ const struct mn10200_operand *operand;
+ expressionS ex;
+
+ if (next_opindex == 0)
+ {
+ operand = &mn10200_operands[*opindex_ptr];
+ }
+ else
+ {
+ operand = &mn10200_operands[next_opindex];
+ next_opindex = 0;
+ }
+
+ errmsg = NULL;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ if (operand->flags & MN10200_OPERAND_PAREN)
+ {
+ if (*input_line_pointer != ')' && *input_line_pointer != '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ input_line_pointer++;
+ goto keep_going;
+ }
+ /* See if we can match the operands. */
+ else if (operand->flags & MN10200_OPERAND_DREG)
+ {
+ if (!data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10200_OPERAND_AREG)
+ {
+ if (!address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10200_OPERAND_PSW)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "psw") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10200_OPERAND_MDR)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "mdr") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (other_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (*str == ')' || *str == '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else
+ {
+ expression (&ex);
+ }
+
+ switch (ex.X_op)
+ {
+ case O_illegal:
+ errmsg = "illegal operand";
+ goto error;
+ case O_absent:
+ errmsg = "missing operand";
+ goto error;
+ case O_register:
+ if ((operand->flags
+ & (MN10200_OPERAND_DREG | MN10200_OPERAND_AREG)) == 0)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ if (opcode->format == FMT_2 || opcode->format == FMT_5)
+ extra_shift = 8;
+ else if (opcode->format == FMT_3 || opcode->format == FMT_6
+ || opcode->format == FMT_7)
+ extra_shift = 16;
+ else
+ extra_shift = 0;
+
+ mn10200_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, (char *) NULL,
+ 0, extra_shift);
+
+ break;
+
+ case O_constant:
+ /* If this operand can be promoted, and it doesn't
+ fit into the allocated bitfield for this insn,
+ then promote it (ie this opcode does not match). */
+ if (operand->flags & MN10200_OPERAND_PROMOTE
+ && ! check_operand (insn, operand, ex.X_add_number))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ mn10200_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, (char *) NULL,
+ 0, 0);
+ break;
+
+ default:
+ /* If this operand can be promoted, then this opcode didn't
+ match since we can't know if it needed promotion! */
+ if (operand->flags & MN10200_OPERAND_PROMOTE)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal ("too many fixups");
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ break;
+ }
+
+keep_going:
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ }
+
+ /* Make sure we used all the operands! */
+ if (*str != ',')
+ match = 1;
+
+ error:
+ if (match == 0)
+ {
+ next_opcode = opcode + 1;
+ if (next_opcode->opcode != 0 && !strcmp(next_opcode->name, opcode->name))
+ {
+ opcode = next_opcode;
+ continue;
+ }
+
+ as_bad ("%s", errmsg);
+ return;
+ }
+ break;
+ }
+
+ while (isspace (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad ("junk at end of line: `%s'", str);
+
+ input_line_pointer = str;
+
+ if (opcode->format == FMT_1)
+ size = 1;
+ else if (opcode->format == FMT_2 || opcode->format == FMT_4)
+ size = 2;
+ else if (opcode->format == FMT_3 || opcode->format == FMT_5)
+ size = 3;
+ else if (opcode->format == FMT_6)
+ size = 4;
+ else if (opcode->format == FMT_7)
+ size = 5;
+ else
+ abort ();
+
+ /* Write out the instruction. */
+
+ f = frag_more (size);
+
+ /* Oh, what a mess. The instruction is in big endian format, but
+ 16 and 24bit immediates are little endian! */
+ if (opcode->format == FMT_3)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_6)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_7)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ number_to_chars_littleendian (f + 4, extension & 0xff, 1);
+ }
+ else
+ {
+ number_to_chars_bigendian (f, insn, size > 4 ? 4 : size);
+ }
+
+#if 0
+ /* Create any fixups. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct mn10200_operand *operand;
+
+ operand = &mn10200_operands[fixups[i].opindex];
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+ int offset;
+ fixS *fixP;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+
+ if (!reloc_howto)
+ abort();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ if (size < 1 || size > 4)
+ abort();
+
+ offset = 4 - size;
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset, size,
+ &fixups[i].exp,
+ reloc_howto->pc_relative,
+ fixups[i].reloc);
+ }
+ else
+ {
+ int reloc, pcrel, reloc_size, offset;
+
+ reloc = BFD_RELOC_NONE;
+ /* How big is the reloc? Remember SPLIT relocs are
+ implicitly 32bits. */
+ reloc_size = operand->bits;
+
+ /* Is the reloc pc-relative? */
+ pcrel = (operand->flags & MN10200_OPERAND_PCREL) != 0;
+
+ /* Gross. This disgusting hack is to make sure we
+ get the right offset for the 16/32 bit reloc in
+ "call" instructions. Basically they're a pain
+ because the reloc isn't at the end of the instruction. */
+ if ((size == 5 || size == 7)
+ && (((insn >> 24) & 0xff) == 0xcd
+ || ((insn >> 24) & 0xff) == 0xdd))
+ size -= 2;
+
+ /* Similarly for certain bit instructions which don't
+ hav their 32bit reloc at the tail of the instruction. */
+ if (size == 7
+ && (((insn >> 16) & 0xffff) == 0xfe00
+ || ((insn >> 16) & 0xffff) == 0xfe01
+ || ((insn >> 16) & 0xffff) == 0xfe02))
+ size -= 1;
+
+ offset = size - reloc_size / 8;
+
+ /* Choose a proper BFD relocation type. */
+ if (pcrel)
+ {
+ if (size == 6)
+ reloc = BFD_RELOC_MN10200_32_PCREL;
+ else if (size == 4)
+ reloc = BFD_RELOC_MN10200_16_PCREL;
+ else if (reloc_size == 32)
+ reloc = BFD_RELOC_32_PCREL;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_16_PCREL;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8_PCREL;
+ else
+ abort ();
+ }
+ else
+ {
+ if (reloc_size == 32)
+ reloc = BFD_RELOC_MN10200_32B;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_MN10200_16B;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8;
+ else
+ abort ();
+ }
+
+ /* Convert the size of the reloc into what fix_new_exp wants. */
+ reloc_size = reloc_size / 8;
+ if (reloc_size == 8)
+ reloc_size = 0;
+ else if (reloc_size == 16)
+ reloc_size = 1;
+ else if (reloc_size == 32)
+ reloc_size = 2;
+
+ fix_new_exp (frag_now, f - frag_now->fr_literal + offset, reloc_size,
+ &fixups[i].exp, pcrel,
+ ((bfd_reloc_code_real_type) reloc));
+ }
+ }
+#endif
+}
+
+
+/* if while processing a fixup, a reloc really needs to be created */
+/* then it is done here */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg;
+ fixS *fixp;
+{
+ arelent *reloc;
+ reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent));
+ reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ "reloc %d not supported by object file format", (int)fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->addend = fixp->fx_offset;
+ /* printf("tc_gen_reloc: addr=%x addend=%x\n", reloc->address, reloc->addend); */
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragp, seg)
+ fragS *fragp;
+ asection *seg;
+{
+ return 0;
+}
+
+long
+md_pcrel_from (fixp)
+ fixS *fixp;
+{
+ return fixp->fx_frag->fr_address;
+#if 0
+ if (fixp->fx_addsy != (symbolS *) NULL && ! S_IS_DEFINED (fixp->fx_addsy))
+ {
+ /* The symbol is undefined. Let the linker figure it out. */
+ return 0;
+ }
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+#endif
+}
+
+int
+md_apply_fix3 (fixp, valuep, seg)
+ fixS *fixp;
+ valueT *valuep;
+ segT seg;
+{
+ /* We shouldn't ever get here because linkrelax is nonzero. */
+ abort ();
+ fixp->fx_done = 1;
+ return 0;
+}
+
+/* Insert an operand value into an instruction. */
+
+static void
+mn10200_insert_operand (insnp, extensionp, operand, val, file, line, shift)
+ unsigned long *insnp;
+ unsigned long *extensionp;
+ const struct mn10200_operand *operand;
+ offsetT val;
+ char *file;
+ unsigned int line;
+ unsigned int shift;
+{
+ /* No need to check 32bit operands for a bit. */
+ if (operand->bits != 32)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & MN10200_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ {
+ const char *err =
+ "operand out of range (%s not between %ld and %ld)";
+ char buf[100];
+
+ sprint_value (buf, test);
+ if (file == (char *) NULL)
+ as_warn (err, buf, min, max);
+ else
+ as_warn_where (file, line, err, buf, min, max);
+ }
+ }
+
+ if ((operand->flags & MN10200_OPERAND_EXTENDED) == 0)
+ {
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10200_OPERAND_REPEATED) != 0)
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + 2));
+ }
+ else
+ {
+ *extensionp |= (val >> 16) & 0xff;
+ *insnp |= val & 0xffff;
+ }
+}
+
+static unsigned long
+check_operand (insn, operand, val)
+ unsigned long insn;
+ const struct mn10200_operand *operand;
+ offsetT val;
+{
+ /* No need to check 32bit operands for a bit. */
+ if (operand->bits != 32)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & MN10200_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ return 0;
+ else
+ return 1;
+ }
+ return 1;
+}