From f1ce6af4fe6a6d71b83af1dde4ad879b93776bcc Mon Sep 17 00:00:00 2001 From: Fred Fish Date: Thu, 30 Jan 1997 20:35:02 +0000 Subject: [PATCH] * config/tc-d10v.c (find_opcode): Remove unused variable "numops". * config/tc-tic80.c: Many additions to previous placeholder file. * config/tc-tic80.h: Ditto. --- gas/ChangeLog | 8 + gas/config/tc-d10v.c | 4 +- gas/config/tc-tic80.c | 603 ++++++++++++++++++++++++++++++++++++++++++ gas/config/tc-tic80.h | 20 +- 4 files changed, 632 insertions(+), 3 deletions(-) diff --git a/gas/ChangeLog b/gas/ChangeLog index d5af7055d29..72df81c1ec1 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,11 @@ +Thu Jan 30 11:46:59 1997 Fred Fish + + * 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 * 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 e22fe27f6cf..1ee15501831 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 54664c149fd..b638d8c47d0 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 4a3a4333145..b29a4e1927c 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 */ -- 2.30.2