📄 tc-i960.c
字号:
} else if (*p == ',') { /* Start of operand */ if (n == 3) { as_bad (_("too many operands")); return -1; } *to++ = '\0'; /* Terminate argument */ args[++n] = to; /* Start next argument */ p++; } else { *to++ = *p++; } } *to = '\0'; return n;}/***************************************************************************** get_cdisp: handle displacement for a COBR or CTRL instruction. Parse displacement for a COBR or CTRL instruction. If successful, output the instruction opcode and set up for it, depending on the arg 'var_frag', either: o an address fixup to be done when all symbol values are known, or o a varying length code fragment, with address fixup info. This will be done for cobr instructions that may have to be relaxed in to compare/branch instructions (8 bytes) if the final address displacement is greater than 13 bits. ****************************************************************************/staticvoidget_cdisp (dispP, ifmtP, instr, numbits, var_frag, callj) /* displacement as specified in source instruction */ char *dispP; /* "COBR" or "CTRL" (for use in error message) */ char *ifmtP; /* Instruction needing the displacement */ long instr; /* # bits of displacement (13 for COBR, 24 for CTRL) */ int numbits; /* 1 if varying length code fragment should be emitted; * 0 if an address fix should be emitted. */ int var_frag; /* 1 if callj relocation should be done; else 0 */ int callj;{ expressionS e; /* Parsed expression */ fixS *fixP; /* Structure describing needed address fix */ char *outP; /* Where instruction binary is output to */ fixP = NULL; parse_expr (dispP, &e); switch (e.X_op) { case O_illegal: as_bad (_("expression syntax error")); case O_symbol: if (S_GET_SEGMENT (e.X_add_symbol) == now_seg || S_GET_SEGMENT (e.X_add_symbol) == undefined_section) { if (var_frag) { outP = frag_more (8); /* Allocate worst-case storage */ md_number_to_chars (outP, instr, 4); frag_variant (rs_machine_dependent, 4, 4, 1, adds (e), offs (e), outP); } else { /* Set up a new fix structure, so address can be updated * when all symbol values are known. */ outP = emit (instr); fixP = fix_new (frag_now, outP - frag_now->fr_literal, 4, adds (e), offs (e), 1, NO_RELOC); fixP->fx_tcbit = callj; /* We want to modify a bit field when the address is * known. But we don't need all the garbage in the * bit_fix structure. So we're going to lie and store * the number of bits affected instead of a pointer. */ fixP->fx_bit_fixP = (bit_fixS *) numbits; } } else as_bad (_("attempt to branch into different segment")); break; default: as_bad (_("target of %s instruction must be a label"), ifmtP); break; }}/***************************************************************************** get_ispec: parse a memory operand for an index specification Here, an "index specification" is taken to be anything surrounded by square brackets and NOT followed by anything else. If it's found, detach it from the input string, remove the surrounding square brackets, and return a pointer to it. Otherwise, return NULL. *************************************************************************** */staticchar *get_ispec (textP) /* Pointer to memory operand from source instruction, no white space. */ char *textP;{ /* Points to start of index specification. */ char *start; /* Points to end of index specification. */ char *end; /* Find opening square bracket, if any. */ start = strchr (textP, '['); if (start != NULL) { /* Eliminate '[', detach from rest of operand */ *start++ = '\0'; end = strchr (start, ']'); if (end == NULL) { as_bad (_("unmatched '['")); } else { /* Eliminate ']' and make sure it was the last thing * in the string. */ *end = '\0'; if (*(end + 1) != '\0') { as_bad (_("garbage after index spec ignored")); } } } return start;}/***************************************************************************** get_regnum: Look up a (suspected) register name in the register table and return the associated register number (or -1 if not found). *************************************************************************** */staticintget_regnum (regname) char *regname; /* Suspected register name */{ int *rP; rP = (int *) hash_find (reg_hash, regname); return (rP == NULL) ? -1 : *rP;}/***************************************************************************** i_scan: perform lexical scan of ascii assembler instruction. Input assumptions: - input string is an i80960 instruction (not a pseudo-op) - all comments and labels have been removed - all strings of whitespace have been collapsed to a single blank. Output: args[0] points to opcode, other entries point to operands. All strings: - are NULL-terminated - contain no whitespace - have character constants ('x') replaced with a decimal number Return value: Number of operands (0,1,2, or 3) or -1 on error. *************************************************************************** */static inti_scan (iP, args) /* Pointer to ascii instruction; MUCKED BY US. */ register char *iP; /* Output arg: pointers to opcode and operands placed here. MUST ACCOMMODATE 4 ENTRIES. */ char *args[];{ /* Isolate opcode */ if (*(iP) == ' ') { iP++; } /* Skip lead space, if any */ args[0] = iP; for (; *iP != ' '; iP++) { if (*iP == '\0') { /* There are no operands */ if (args[0] == iP) { /* We never moved: there was no opcode either! */ as_bad (_("missing opcode")); return -1; } return 0; } } *iP++ = '\0'; /* Terminate opcode */ return (get_args (iP, args));} /* i_scan() *//***************************************************************************** mem_fmt: generate a MEMA- or MEMB-format instruction *************************************************************************** */static voidmem_fmt (args, oP, callx) char *args[]; /* args[0]->opcode mnemonic, args[1-3]->operands */ struct i960_opcode *oP; /* Pointer to description of instruction */ int callx; /* Is this a callx opcode */{ int i; /* Loop counter */ struct regop regop; /* Description of register operand */ char opdesc; /* Operand descriptor byte */ memS instr; /* Description of binary to be output */ char *outP; /* Where the binary was output to */ expressionS expr; /* Parsed expression */ /* ->description of deferred address fixup */ fixS *fixP;#ifdef OBJ_COFF /* COFF support isn't in place yet for callx relaxing. */ callx = 0;#endif memset (&instr, '\0', sizeof (memS)); instr.opcode = oP->opcode; /* Process operands. */ for (i = 1; i <= oP->num_ops; i++) { opdesc = oP->operand[i - 1]; if (MEMOP (opdesc)) { parse_memop (&instr, args[i], oP->format); } else { parse_regop (®op, args[i], opdesc); instr.opcode |= regop.n << 19; } } /* Parse the displacement; this must be done before emitting the opcode, in case it is an expression using `.'. */ parse_expr (instr.e, &expr); /* Output opcode */ outP = emit (instr.opcode); if (instr.disp == 0) { return; } /* Process the displacement */ switch (expr.X_op) { case O_illegal: as_bad (_("expression syntax error")); break; case O_constant: if (instr.disp == 32) { (void) emit (offs (expr)); /* Output displacement */ } else { /* 12-bit displacement */ if (offs (expr) & ~0xfff) { /* Won't fit in 12 bits: convert already-output * instruction to MEMB format, output * displacement. */ mema_to_memb (outP); (void) emit (offs (expr)); } else { /* WILL fit in 12 bits: OR into opcode and * overwrite the binary we already put out */ instr.opcode |= offs (expr); md_number_to_chars (outP, instr.opcode, 4); } } break; default: if (instr.disp == 12) { /* Displacement is dependent on a symbol, whose value * may change at link time. We HAVE to reserve 32 bits. * Convert already-output opcode to MEMB format. */ mema_to_memb (outP); } /* Output 0 displacement and set up address fixup for when * this symbol's value becomes known. */ outP = emit ((long) 0); fixP = fix_new_exp (frag_now, outP - frag_now->fr_literal, 4, &expr, 0, NO_RELOC); /* Steve's linker relaxing hack. Mark this 32-bit relocation as being in the instruction stream, specifically as part of a callx instruction. */ fixP->fx_bsr = callx; break; }} /* memfmt() *//***************************************************************************** mema_to_memb: convert a MEMA-format opcode to a MEMB-format opcode. There are 2 possible MEMA formats: - displacement only - displacement + abase They are distinguished by the setting of the MEMA_ABASE bit. *************************************************************************** */static voidmema_to_memb (opcodeP) char *opcodeP; /* Where to find the opcode, in target byte order */{ long opcode; /* Opcode in host byte order */ long mode; /* Mode bits for MEMB instruction */ opcode = md_chars_to_number (opcodeP, 4); know (!(opcode & MEMB_BIT)); mode = MEMB_BIT | D_BIT; if (opcode & MEMA_ABASE) { mode |= A_BIT; } opcode &= 0xffffc000; /* Clear MEMA offset and mode bits */ opcode |= mode; /* Set MEMB mode bits */ md_number_to_chars (opcodeP, opcode, 4);} /* mema_to_memb() *//***************************************************************************** parse_expr: parse an expression Use base assembler's expression parser to parse an expression. It, unfortunately, runs off a global which we have to save/restore in order to make it work for us. An empty expression string is treated as an absolute 0. Sets O_illegal regardless of expression evaluation if entire input string is not consumed in the evaluation -- tolerate no dangling junk! *************************************************************************** */static voidparse_expr (textP, expP) char *textP; /* Text of expression to be parsed */ expressionS *expP; /* Where to put the results of parsing */{ char *save_in; /* Save global here */ symbolS *symP; know (textP); if (*textP == '\0') { /* Treat empty string as absolute 0 */ expP->X_add_symbol = expP->X_op_symbol = NULL; expP->X_add_number = 0; expP->X_op = O_constant; } else { save_in = input_line_pointer; /* Save global */ input_line_pointer = textP; /* Make parser work for us */ (void) expression (expP); if ((size_t) (input_line_pointer - textP) != strlen (textP)) { /* Did not consume all of the input */ expP->X_op = O_illegal; } symP = expP->X_add_symbol; if (symP && (hash_find (reg_hash, S_GET_NAME (symP)))) { /* Register name in an expression */ /* FIXME: this isn't much of a check any more. */ expP->X_op = O_illegal; } input_line_pointer = save_in; /* Restore global */ }}/***************************************************************************** parse_ldcont: Parse and replace a 'ldconst' pseudo-instruction with an appropriate i80960 instruction. Assumes the input consists of: arg[0] opcode mnemonic ('ldconst') arg[1] first operand (constant) arg[2] name of register to be loaded Replaces opcode and/or operands as appropriate. Returns the new number of arguments, or -1 on failure. *************************************************************************** */staticintparse_ldconst (arg) char *arg[]; /* See above */{ int n; /* Constant to be loaded */ int shift; /* Shift count for "shlo" instruction */ static char buf[5]; /* Literal for first operand */ static char buf2[5]; /* Literal for second operand */ expressionS e; /* Parsed expression */ arg[3] = NULL; /* So we can tell at the end if it got used or not */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -