aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
Diffstat (limited to 'gas')
-rw-r--r--gas/ChangeLog8
-rw-r--r--gas/config/tc-d10v.c4
-rw-r--r--gas/config/tc-tic80.c603
-rw-r--r--gas/config/tc-tic80.h20
4 files changed, 632 insertions, 3 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index d5af705..72df81c 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,11 @@
+Thu Jan 30 11:46:59 1997 Fred Fish <fnf@cygnus.com>
+
+ * config/tc-d10v.c (find_opcode): Remove unused variable "numops".
+start-sanitize-tic80
+ * config/tc-tic80.c: Many additions to previous placeholder file.
+ * config/tc-tic80.h: Ditto.
+end-sanitize-tic80
+
Thu Jan 30 12:28:18 1997 Alan Modra <alan@spri.levels.unisa.edu.au>
* config/tc-i386.c (i386_align_code): Improve the nop patterns.
diff --git a/gas/config/tc-d10v.c b/gas/config/tc-d10v.c
index e22fe27..1ee1550 100644
--- a/gas/config/tc-d10v.c
+++ b/gas/config/tc-d10v.c
@@ -1057,11 +1057,11 @@ find_opcode (opcode, myops)
struct d10v_opcode *opcode;
expressionS myops[];
{
- int i, match, done, numops;
+ int i, match, done;
struct d10v_opcode *next_opcode;
/* get all the operands and save them as expressions */
- numops = get_operands (myops);
+ get_operands (myops);
/* now see if the operand is a fake. If so, find the correct size */
/* instruction, if possible */
diff --git a/gas/config/tc-tic80.c b/gas/config/tc-tic80.c
index 54664c1..b638d8c 100644
--- a/gas/config/tc-tic80.c
+++ b/gas/config/tc-tic80.c
@@ -42,5 +42,608 @@ const char EXP_CHARS[] = "eE";
as in 0d1.0. */
const char FLT_CHARS[] = "dD";
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"word", cons, 4},
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *tic80_hash;
+
+static struct tic80_opcode * find_opcode PARAMS ((struct tic80_opcode *, expressionS []));
+static unsigned long build_insn PARAMS ((struct tic80_opcode *, expressionS *, unsigned long insn));
+static int get_operands PARAMS ((expressionS exp[]));
+static int reg_name_search PARAMS ((char *name));
+static int register_name PARAMS ((expressionS *expressionP));
+
+int
+md_estimate_size_before_relax (fragP, segment_type)
+ fragS *fragP;
+ segT segment_type;
+{
+ as_fatal ("Relaxation is a luxury we can't afford\n");
+ return (-1);
+}
+
+/* We have no need to default values of symbols. */
+
+/* ARGSUSED */
+symbolS *
+md_undefined_symbol (name)
+ char *name;
+{
+ return 0;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant of type
+ type, and store the appropriate bytes in *litP. The number of LITTLENUMS
+ emitted is stored in *sizeP . An error message is returned, or NULL on OK.
+ */
+
+#define MAX_LITTLENUMS 4
+
+char *
+md_atof (type, litP, sizeP)
+ int type;
+ char *litP;
+ int *sizeP;
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ char *atof_ieee ();
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ 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 * sizeof (LITTLENUM_TYPE);
+
+ for (wordP = words; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return (NULL);
+}
+
+/* reg_name_search does a binary search of the tic80_pre_defined_registers
+ array 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 (name)
+ char *name;
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = tic80_num_regs - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, tic80_pre_defined_registers[middle].name);
+ if (cmp < 0)
+ {
+ high = middle - 1;
+ }
+ else if (cmp > 0)
+ {
+ low = middle + 1;
+ }
+ else
+ {
+ return (tic80_pre_defined_registers[middle].value);
+ }
+ }
+ while (low <= high);
+ return (-1);
+}
+
+/* register_name() checks the string at input_line_pointer
+ to see if it is a valid register name */
+
+static int
+register_name (expressionP)
+ expressionS *expressionP;
+{
+ int reg_number;
+ char c;
+ char *p = input_line_pointer;
+
+ while (*p != '\000' && *p != '\n' && *p != '\r' && *p != ',' && *p != ' ' && *p != ')')
+ p++;
+
+ c = *p;
+ if (c)
+ {
+ *p++ = '\000';
+ }
+
+ /* look to see if it's in the register table */
+
+ reg_number = reg_name_search (input_line_pointer);
+ if (reg_number >= 0)
+ {
+ expressionP -> X_op = O_register;
+ /* temporarily store a pointer to the string here */
+ expressionP -> X_op_symbol = (struct symbol *) input_line_pointer;
+ expressionP -> X_add_number = reg_number;
+ input_line_pointer = p;
+ return (1);
+ }
+ if (c)
+ {
+ *(p - 1) = c;
+ }
+ return (0);
+}
+
+/* get_operands() parses a string of operands and fills in a passed array of
+ expressions in EXP.
+
+ Note that we use O_absent expressions to record additional information
+ about the previous non-O_absent expression, such as ":m" or ":s"
+ modifiers or register numbers enclosed in parens like "(r10)".
+
+ Returns the number of expressions that were placed in EXP.
+
+ */
+
+static int
+get_operands (exp)
+ expressionS exp[];
+{
+ char *p = input_line_pointer;
+ int numexp = 0;
+ int mflag = 0;
+ int sflag = 0;
+ int parens = 0;
+
+ while (*p)
+ {
+ /* Skip leading whitespace */
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ {
+ p++;
+ }
+
+ /* Check to see if we have any operands left to parse */
+ if (*p == 0 || *p == '\n' || *p == '\r')
+ {
+ break;
+ }
+
+ /* Notice scaling or direct memory operand modifiers and save them in
+ an O_absent expression after the expression that they modify. */
+
+ if (*p == ':')
+ {
+ p++;
+ exp[numexp].X_op = O_absent;
+ if (*p == 'm')
+ {
+ p++;
+ exp[numexp].X_add_number = TIC80_OPERAND_M_SI | TIC80_OPERAND_M_LI;
+ }
+ else if (*p == 's')
+ {
+ p++;
+ exp[numexp].X_add_number = TIC80_OPERAND_SCALED;
+ }
+ else
+ {
+ as_bad ("':' not followed by 'm' or 's'");
+ }
+ numexp++;
+ continue;
+ }
+
+ /* Handle leading '(' on operands that use them, by recording that we
+ have entered a paren nesting level and then continuing. We complain
+ about multiple nesting. */
+
+ if (*p == '(')
+ {
+ if (++parens != 1)
+ {
+ as_bad ("paren nesting");
+ }
+ p++;
+ continue;
+ }
+
+ /* Handle trailing ')' on operands that use them, by reducing the
+ nesting level and then continuing. We complain if there were too
+ many closures. */
+
+ if (*p == ')')
+ {
+ /* Record that we have left a paren group and continue */
+ if (--parens < 0)
+ {
+ as_bad ("mismatched parenthesis");
+ }
+ p++;
+ continue;
+ }
+
+ /* Begin operand parsing at the current scan point. */
+
+ input_line_pointer = p;
+
+ /* Check to see if it might be a register name */
+
+ if (!register_name (&exp[numexp]))
+ {
+ /* parse as an expression */
+ expression (&exp[numexp]);
+ }
+
+ if (exp[numexp].X_op == O_illegal)
+ {
+ as_bad ("illegal operand");
+ }
+ else if (exp[numexp].X_op == O_absent)
+ {
+ as_bad ("missing operand");
+ }
+
+ numexp++;
+ p = input_line_pointer;
+ }
+
+ if (parens)
+ {
+ exp[numexp].X_op = O_absent;
+ exp[numexp++].X_add_number = TIC80_OPERAND_PARENS;
+ }
+
+ exp[numexp].X_op = 0;
+ return (numexp);
+}
+
+/* find_opcode() gets a pointer to the entry in the opcode table that
+ matches the instruction being assembled, or returns NULL if no such match
+ is found.
+
+ First it parses all the operands and save them as expressions. Note that
+ we use O_absent expressions to record additional information about the
+ previous non-O_absent expression, such as ":m" or ":s" modifiers or
+ register numbers enclosed in parens like "(r10)".
+
+ It then looks at all opcodes with the same name and use the operands to
+ choose the correct opcode. */
+
+
+static struct tic80_opcode *
+find_opcode (opcode, myops)
+ struct tic80_opcode *opcode;
+ expressionS myops[];
+{
+ int i, match, done, numops;
+ struct tic80_opcode *next_opcode;
+
+ match = 0;
+
+ /* First parse all the operands so we only have to do it once. */
+
+ get_operands (myops);
+
+ /* Now search the opcode table table for one with operands that
+ matches what we've got. */
+
+ while (!match)
+ {
+ match = 1;
+ for (i = 0; opcode -> operands[i]; i++)
+ {
+ int flags = tic80_operands[opcode->operands[i]].flags;
+ int X_op = myops[i].X_op;
+ int num = myops[i].X_add_number;
+
+ if (X_op == 0)
+ {
+ match = 0;
+ break;
+ }
+
+ if (flags & TIC80_OPERAND_GPR)
+ {
+ if ((X_op != O_register) ||
+ ((flags & OPERAND_ACC) != (num & OPERAND_ACC)) ||
+ ((flags & OPERAND_FLAG) != (num & OPERAND_FLAG)) ||
+ ((flags & OPERAND_CONTROL) != (num & OPERAND_CONTROL)))
+ {
+ match=0;
+ break;
+ }
+ }
+
+ if (((flags & OPERAND_MINUS) && ((X_op != O_absent) || (num != OPERAND_MINUS))) ||
+ ((flags & OPERAND_PLUS) && ((X_op != O_absent) || (num != OPERAND_PLUS))) ||
+ ((flags & OPERAND_ATMINUS) && ((X_op != O_absent) || (num != OPERAND_ATMINUS))) ||
+ ((flags & OPERAND_ATPAR) && ((X_op != O_absent) || (num != OPERAND_ATPAR))) ||
+ ((flags & OPERAND_ATSIGN) && ((X_op != O_absent) || (num != OPERAND_ATSIGN))))
+ {
+ match=0;
+ break;
+ }
+ }
+ /* we're only done if the operands matched so far AND there
+ are no more to check */
+ if (match && myops[i].X_op==0)
+ break;
+ else
+ match = 0;
+
+ next_opcode = opcode+1;
+ if (next_opcode->opcode == 0)
+ break;
+ if (strcmp(next_opcode->name, opcode->name))
+ break;
+ opcode = next_opcode;
+ }
+
+ if (!match)
+ {
+ as_bad ("bad opcode or operands");
+ return (0);
+ }
+
+ /* Check that all registers that are required to be even are. */
+ /* Also, if any operands were marked as registers, but were really symbols */
+ /* fix that here. */
+ for (i=0; opcode->operands[i]; i++)
+ {
+ if ((tic80_operands[opcode->operands[i]].flags & OPERAND_EVEN) &&
+ (myops[i].X_add_number & 1))
+ as_fatal("Register number must be EVEN");
+ if (myops[i].X_op == O_register)
+ {
+ if (!(tic80_operands[opcode->operands[i]].flags & OPERAND_REG))
+ {
+ myops[i].X_op = O_symbol;
+ myops[i].X_add_symbol = symbol_find_or_make ((char *)myops[i].X_op_symbol);
+ myops[i].X_add_number = 0;
+ myops[i].X_op_symbol = NULL;
+ }
+ }
+ }
+ return opcode;
+}
+
+/* build_insn takes a pointer to the opcode entry in the opcode table
+ and the array of operand expressions and returns the instruction */
+
+static unsigned long
+build_insn (opcode, opers, insn)
+ struct tic80_opcode *opcode;
+ expressionS *opers;
+ unsigned long insn;
+{
+ return (0);
+}
+
+/* This is the main entry point for the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit the frags/bytes
+ it assembles to.
+ */
+
+void
+md_assemble (str)
+ char *str;
+{
+ char *scan;
+ unsigned char *input_line_save;
+ struct tic80_opcode *opcode;
+ expressionS myops[6];
+ unsigned long insn;
+
+ /* Ensure there is something there to assemble. */
+ assert (str);
+
+ /* Drop any leading whitespace. */
+ while (isspace (*str))
+ {
+ str++;
+ }
+
+ /* Isolate the mnemonic from the rest of the string by finding the first
+ whitespace character and zapping it to a null byte. */
+ for (scan = str; *scan != '\000' && !isspace (*scan); scan++) {;}
+ if (*scan != '\000')
+ {
+ *scan++ = '\000';
+ }
+
+ /* Try to find this mnemonic in the hash table */
+ if ((opcode = (struct tic80_opcode *) hash_find (tic80_hash, str)) == NULL)
+ {
+ as_bad ("Invalid mnemonic: '%s'", str);
+ return;
+ }
+
+ str = scan;
+ while (isspace (*scan))
+ {
+ scan++;
+ }
+
+ input_line_save = input_line_pointer;
+ input_line_pointer = str;
+
+ opcode = find_opcode (opcode, myops);
+ if (opcode == NULL)
+ {
+ return;
+ }
+
+ input_line_pointer = input_line_save;
+ insn = build_insn (opcode, myops, 0);
+
+ /* FIXME - finish this */
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc., that the MD part of the assembler will
+ need. */
+
+void
+md_begin ()
+{
+ char *prev_name = "";
+ register const struct tic80_opcode *op;
+ register const struct tic80_opcode *op_end;
+
+ tic80_hash = hash_new();
+
+ /* Insert unique names into hash table. The TIc80 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_end = tic80_opcodes + tic80_num_opcodes;
+ for (op = tic80_opcodes; op < op_end; op++)
+ {
+ if (strcmp (prev_name, op -> name) != 0)
+ {
+ prev_name = (char *) op -> name;
+ hash_insert (tic80_hash, op -> name, (char *) op);
+ }
+ }
+}
+
+
+
+CONST char *md_shortopts = "";
+
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof(md_longopts);
+
+/* Take care of the target-specific command-line options. */
+
+int
+md_parse_option (c, arg)
+ int c;
+ char *arg;
+{
+ return (0);
+}
+
+/* Print a description of the command-line options that we accept. */
+
+void
+md_show_usage (stream)
+ FILE *stream;
+{
+}
+
+
+/* Attempt to simplify or even eliminate a fixup. The return value is
+ ignored; perhaps it was once meaningful, but now it is historical.
+ To indicate that a fixup has been eliminated, set fixP->fx_done.
+ */
+
+void
+md_apply_fix (fixP, val)
+ fixS *fixP;
+ long val;
+{
+ as_fatal ("md_apply_fix() not implemented yet\n");
+ abort ();
+}
+
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from (fixP)
+ fixS *fixP;
+{
+ as_fatal ("md_pcrel_from() not implemented yet\n");
+ abort ();
+}
+
+/*
+ * Called after relax() is finished.
+ * In: Address of frag.
+ * fr_type == rs_machine_dependent.
+ * fr_subtype is what the address relaxed to.
+ *
+ * Out: Any fixSs and constants are set up.
+ * Caller will turn frag into a ".space 0".
+ */
+
+void
+md_convert_frag (headers, seg, fragP)
+ object_headers *headers;
+ segT seg;
+ fragS *fragP;
+{
+ as_fatal ("md_convert_frag() not implemented yet\n");
+ abort ();
+}
+
+
+/*ARGSUSED*/
+void
+tc_coff_symbol_emit_hook (ignore)
+ symbolS *ignore;
+{
+}
+
+#if defined OBJ_COFF
+
+short
+tc_coff_fix2rtype (fixP)
+ fixS *fixP;
+{
+ as_fatal ("tc_coff_fix2rtype() not implemented yet\n");
+ abort ();
+}
+
+#endif /* OBJ_COFF */
+
/* end of tc-tic80.c */
diff --git a/gas/config/tc-tic80.h b/gas/config/tc-tic80.h
index 4a3a433..b29a4e1 100644
--- a/gas/config/tc-tic80.h
+++ b/gas/config/tc-tic80.h
@@ -22,6 +22,24 @@
#define TARGET_ARCH bfd_arch_tic80
#define BFD_ARCH TARGET_ARCH
-#define COFF_MAGIC TIC80MAGIC
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* We have no special operand handling. */
+#define md_operand(x)
+
+#ifdef OBJ_COFF
+/* COFF specific definitions. */
+
+#define COFF_MAGIC TIC80_ARCH_MAGIC
+
+/* Whether a reloc should be output. */
+#define TC_COUNT_RELOC(fixp) ((fixp) -> fx_addsy != NULL)
+
+/* This macro translates between an internal fix and an coff reloc type */
+#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype(fixP)
+extern short tc_coff_fix2rtype ();
+
+#endif /* OBJ_COFF */
/* end of tc-tic80.h */