📄 tc-tic80.c
字号:
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 & TIC80_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 & TIC80_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; } } }#endif}/* build_insn takes a pointer to the opcode entry in the opcode table and the array of operand expressions and writes out the instruction. Note that the opcode word and extended word may be written to different frags, with the opcode at the end of one frag and the extension at the beginning of the next. */static voidbuild_insn (opcode, opers) struct tic80_opcode *opcode; expressionS *opers;{ int expi; /* Index of current expression to match */ int opi; /* Index of current operand to match */ unsigned long insn[2]; /* Instruction and long immediate (if any) */ char *f; /* Pointer to frag location for insn[0] */ fragS *ffrag; /* Frag containing location f */ char *fx = NULL; /* Pointer to frag location for insn[1] */ fragS *fxfrag; /* Frag containing location fx */ /* Start with the raw opcode bits from the opcode table. */ insn[0] = opcode->opcode; /* We are going to insert at least one 32 bit opcode so get the frag now. */ f = frag_more (4); ffrag = frag_now; /* For each operand expression, insert the appropriate bits into the instruction. */ for (expi = 0, opi = -1; opers[expi].X_op != O_illegal; expi++) { int bits, shift, flags, X_op, num; X_op = opers[expi].X_op; num = opers[expi].X_add_number; /* The O_absent expressions apply to the same operand as the most recent non O_absent expression. So only increment the operand index when the current expression is not one of these special expressions. */ if (X_op != O_absent) { opi++; } flags = tic80_operands[opcode->operands[opi]].flags; bits = tic80_operands[opcode->operands[opi]].bits; shift = tic80_operands[opcode->operands[opi]].shift; switch (X_op) { case O_register: num &= ~TIC80_OPERAND_MASK; insn[0] = insn[0] | (num << shift); break; case O_constant: if ((flags & TIC80_OPERAND_ENDMASK) && (num == 32)) { /* Endmask values of 0 and 32 give identical results. */ num = 0; } else if ((flags & TIC80_OPERAND_BITNUM)) { /* BITNUM values are stored in one's complement form. */ num = (~num & 0x1F); } /* Mask off upper bits, just it case it is signed and is negative. */ if (bits < 32) { num &= (1 << bits) - 1; insn[0] = insn[0] | (num << shift); } else { fx = frag_more (4); fxfrag = frag_now; insn[1] = num; } break; case O_symbol: if (bits == 32) { fx = frag_more (4); fxfrag = frag_now; insn[1] = 0; if (flags & TIC80_OPERAND_PCREL) { fix_new_exp (fxfrag, fx - (fxfrag->fr_literal), 4, &opers[expi], 1, R_MPPCR); } else { fix_new_exp (fxfrag, fx - (fxfrag->fr_literal), 4, &opers[expi], 0, R_RELLONGX); } } else if (flags & TIC80_OPERAND_PCREL) { fix_new_exp (ffrag, f - (ffrag->fr_literal), 4, /* FIXME! how is this used? */ &opers[expi], 1, R_MPPCR15W); } else { internal_error (_("symbol reloc that is not PC relative or 32 bits")); } break; case O_absent: /* Each O_absent expression can indicate exactly one possible modifier. */ if ((num & TIC80_OPERAND_M_SI) && (flags & TIC80_OPERAND_M_SI)) { insn[0] = insn[0] | (1 << 17); } else if ((num & TIC80_OPERAND_M_LI) && (flags & TIC80_OPERAND_M_LI)) { insn[0] = insn[0] | (1 << 15); } else if ((num & TIC80_OPERAND_SCALED) && (flags & TIC80_OPERAND_SCALED)) { insn[0] = insn[0] | (1 << 11); } else if ((num & TIC80_OPERAND_PARENS) && (flags & TIC80_OPERAND_PARENS)) { /* No code to generate, just accept and discard this expression. */ } else { internal_error_a (_("unhandled operand modifier"), opers[expi].X_add_number); } break; case O_big: fx = frag_more (4); fxfrag = frag_now; { int precision = 2; long exponent_bits = 8L; LITTLENUM_TYPE words[2]; /* Value is still in generic_floating_point_number. */ gen_to_words (words, precision, exponent_bits); insn[1] = (words[0] << 16) | words[1]; } break; case O_illegal: case O_symbol_rva: case O_uminus: case O_bit_not: case O_logical_not: case O_multiply: case O_divide: case O_modulus: case O_left_shift: case O_right_shift: case O_bit_inclusive_or: case O_bit_or_not: case O_bit_exclusive_or: case O_bit_and: case O_add: case O_subtract: case O_eq: case O_ne: case O_lt: case O_le: case O_ge: case O_gt: case O_logical_and: case O_logical_or: case O_max: default: internal_error_a (_("unhandled expression"), X_op); break; } } /* Write out the instruction, either 4 or 8 bytes. */ md_number_to_chars (f, insn[0], 4); if (fx != NULL) { md_number_to_chars (fx, insn[1], 4); }}/* This is the main entry point for the machine-dependent assembler. Gas calls this function for each input line which does not contain a pseudoop. STR points to a NULL terminated machine dependent instruction. This function is supposed to emit the frags/bytes it assembles to. */voidmd_assemble (str) char *str;{ char *scan; unsigned char *input_line_save; struct tic80_opcode *opcode; expressionS myops[16]; 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) as_bad (_("Invalid operands: '%s'"), input_line_save); input_line_pointer = input_line_save; build_insn (opcode, myops);}/* This function is called once at the start of assembly, after the command line arguments have been parsed and all the machine independent initializations have been completed. It should set up all the tables, etc., that the machine dependent part of the assembler will need. */voidmd_begin (){ char *prev_name = ""; register const struct tic80_opcode *op; register const struct tic80_opcode *op_end; const struct predefined_symbol *pdsp; extern int coff_flags; /* Defined in obj-coff.c */ /* Set F_AR32WR in coff_flags, which will end up in the file header f_flags field. */ coff_flags |= F_AR32WR; /* TIc80 is 32 bit little endian. */ /* 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. */ tic80_hash = hash_new (); 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); } } /* Insert the predefined symbols into the symbol table. We use symbol_create rather than symbol_new so that these symbols don't end up in the object files' symbol table. Note that the values of the predefined symbols include some upper bits that distinguish the type of the symbol (register, bitnum, condition code, etc) and these bits must be masked away before actually inserting the values into the instruction stream. For registers we put these bits in the symbol table since we use them later and there is no question that they aren't part of the register number. For constants we can't do that since the constant can be any value, so they are masked off before putting them into the symbol table. */ pdsp = NULL; while ((pdsp = tic80_next_predefined_symbol (pdsp)) != NULL) { segT segment; valueT valu; int symtype; symtype = PDS_VALUE (pdsp) & TIC80_OPERAND_MASK; switch (symtype) { case TIC80_OPERAND_GPR: case TIC80_OPERAND_FPA: case TIC80_OPERAND_CR: segment = reg_section; valu = PDS_VALUE (pdsp); break; case TIC80_OPERAND_CC: case TIC80_OPERAND_BITNUM: segment = absolute_section; valu = PDS_VALUE (pdsp) & ~TIC80_OPERAND_MASK; break; default: internal_error_a (_("unhandled predefined symbol bits"), symtype); break; } symbol_table_insert (symbol_create (PDS_NAME (pdsp), segment, valu, &zero_address_frag)); }}/* The assembler adds md_shortopts to the string passed to getopt. */CONST char *md_shortopts = "";/* The assembler adds md_longopts to the machine independent long options that are passed to getopt. */struct option md_longopts[] = {#define OPTION_RELAX (OPTION_MD_BASE) {"relax", no_argument, NULL, OPTION_RELAX},#define OPTION_NO_RELAX (OPTION_RELAX + 1) {"no-relax", no_argument, NULL, OPTION_NO_RELAX}, {NULL, no_argument, NULL, 0}};size_t md_longopts_size = sizeof (md_longopts);/* The md_parse_option function will be called whenever getopt returns an unrecognized code, presumably indicating a special code value which appears in md_longopts for machine specific command line options. */intmd_parse_option (c, arg) int c; char *arg;{ switch (c) { case OPTION_RELAX: tic80_relax = 1; break; case OPTION_NO_RELAX: tic80_relax = 0; break; default: return (0); } return (1);}/* The md_show_usage function will be called whenever a usage message is printed. It should print a description of the machine specific options found in md_longopts. */voidmd_show_usage (stream) FILE *stream;{ fprintf (stream, "\TIc80 options:\n\-relax alter PC relative branch instructions to use long form when needed\n\-no-relax always use short PC relative branch instructions, error on overflow\n");}/* 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. */voidmd_apply_fix (fixP, val) fixS *fixP; long val;{ char *dest = fixP->fx_frag->fr_literal + fixP->fx_where; int overflow; switch (fixP->fx_r_type) { case R_RELLONGX: md_number_to_chars (dest, (valueT) val, 4); break; case R_MPPCR: val >>= 2; val += 1; /* Target address computed from inst start */ md_number_to_chars (dest, (valueT) val, 4); break; case R_MPPCR15W: overflow = (val < -65536L) || (val > 65532L); if (overflow) { as_bad_where (fixP->fx_file, fixP->fx_line, _("PC offset 0x%lx outside range 0x%lx-0x%lx"), val, -65536L, 65532L); } else { val >>= 2; *dest++ = val & 0xFF; val >>= 8; *dest = (*dest & 0x80) | (val & 0x7F); } break; case R_ABS: md_number_to_chars (dest, (valueT) val, fixP->fx_size); break; default: internal_error_a (_("unhandled relocation type in fixup"), fixP->fx_r_type); break; }}/* Functions concerning relocs. *//* The location from which a PC relative jump should be calculated, given a PC relative reloc. For the TIc80, this is the address of the 32 bit opcode containing the PC relative field. */longmd_pcrel_from (fixP) fixS *fixP;{ return (fixP->fx_frag->fr_address + fixP->fx_where);}/* 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". */voidmd_convert_frag (headers, seg, fragP) object_headers *headers; segT seg; fragS *fragP;{ internal_error (_("md_convert_frag() not implemented yet")); abort ();}voidtc_coff_symbol_emit_hook (ignore) symbolS *ignore;{}#if defined OBJ_COFFshorttc_coff_fix2rtype (fixP) fixS *fixP;{ return (fixP->fx_r_type);}#endif /* OBJ_COFF */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -