📄 tc-sh.c
字号:
provided. */static sh_opcode_info *get_specific (opcode, operands) sh_opcode_info *opcode; sh_operand_info *operands;{ sh_opcode_info *this_try = opcode; char *name = opcode->name; int n = 0; while (opcode->name) { this_try = opcode++; if (this_try->name != name) { /* We've looked so far down the table that we've run out of opcodes with the same name. */ return 0; } /* Look at both operands needed by the opcodes and provided by the user - since an arg test will often fail on the same arg again and again, we'll try and test the last failing arg the first on each opcode try. */ for (n = 0; this_try->arg[n]; n++) { sh_operand_info *user = operands + n; sh_arg_type arg = this_try->arg[n]; switch (arg) { case A_IMM: case A_BDISP12: case A_BDISP8: case A_DISP_GBR: case A_DISP_PC: case A_MACH: case A_PR: case A_MACL: if (user->type != arg) goto fail; break; case A_R0: /* opcode needs r0 */ if (user->type != A_REG_N || user->reg != 0) goto fail; break; case A_R0_GBR: if (user->type != A_R0_GBR || user->reg != 0) goto fail; break; case F_FR0: if (user->type != F_REG_N || user->reg != 0) goto fail; break; case A_REG_N: case A_INC_N: case A_DEC_N: case A_IND_N: case A_IND_R0_REG_N: case A_DISP_REG_N: case F_REG_N: case D_REG_N: case X_REG_N: case V_REG_N: case FPUL_N: case FPSCR_N: case A_PMOD_N: case A_PMODY_N: case DSP_REG_N: /* Opcode needs rn */ if (user->type != arg) goto fail; reg_n = user->reg; break; case DX_REG_N: if (user->type != D_REG_N && user->type != X_REG_N) goto fail; reg_n = user->reg; break; case A_GBR: case A_SR: case A_VBR: case A_DSR: case A_MOD: case A_RE: case A_RS: case A_SSR: case A_SPC: case A_SGR: case A_DBR: if (user->type != arg) goto fail; break; case A_REG_B: if (user->type != arg) goto fail; reg_b = user->reg; break; case A_REG_M: case A_INC_M: case A_DEC_M: case A_IND_M: case A_IND_R0_REG_M: case A_DISP_REG_M: case DSP_REG_M: /* Opcode needs rn */ if (user->type != arg - A_REG_M + A_REG_N) goto fail; reg_m = user->reg; break; case DSP_REG_X: if (user->type != DSP_REG_N) goto fail; switch (user->reg) { case A_X0_NUM: reg_x = 0; break; case A_X1_NUM: reg_x = 1; break; case A_A0_NUM: reg_x = 2; break; case A_A1_NUM: reg_x = 3; break; default: goto fail; } break; case DSP_REG_Y: if (user->type != DSP_REG_N) goto fail; switch (user->reg) { case A_Y0_NUM: reg_y = 0; break; case A_Y1_NUM: reg_y = 1; break; case A_M0_NUM: reg_y = 2; break; case A_M1_NUM: reg_y = 3; break; default: goto fail; } break; case DSP_REG_E: if (user->type != DSP_REG_N) goto fail; switch (user->reg) { case A_X0_NUM: reg_efg = 0 << 10; break; case A_X1_NUM: reg_efg = 1 << 10; break; case A_Y0_NUM: reg_efg = 2 << 10; break; case A_A1_NUM: reg_efg = 3 << 10; break; default: goto fail; } break; case DSP_REG_F: if (user->type != DSP_REG_N) goto fail; switch (user->reg) { case A_Y0_NUM: reg_efg |= 0 << 8; break; case A_Y1_NUM: reg_efg |= 1 << 8; break; case A_X0_NUM: reg_efg |= 2 << 8; break; case A_A1_NUM: reg_efg |= 3 << 8; break; default: goto fail; } break; case DSP_REG_G: if (user->type != DSP_REG_N) goto fail; switch (user->reg) { case A_M0_NUM: reg_efg |= 0 << 2; break; case A_M1_NUM: reg_efg |= 1 << 2; break; case A_A0_NUM: reg_efg |= 2 << 2; break; case A_A1_NUM: reg_efg |= 3 << 2; break; default: goto fail; } break; case A_A0: if (user->type != DSP_REG_N || user->reg != A_A0_NUM) goto fail; break; case A_X0: if (user->type != DSP_REG_N || user->reg != A_X0_NUM) goto fail; break; case A_X1: if (user->type != DSP_REG_N || user->reg != A_X1_NUM) goto fail; break; case A_Y0: if (user->type != DSP_REG_N || user->reg != A_Y0_NUM) goto fail; break; case A_Y1: if (user->type != DSP_REG_N || user->reg != A_Y1_NUM) goto fail; break; case F_REG_M: case D_REG_M: case X_REG_M: case V_REG_M: case FPUL_M: case FPSCR_M: /* Opcode needs rn */ if (user->type != arg - F_REG_M + F_REG_N) goto fail; reg_m = user->reg; break; case DX_REG_M: if (user->type != D_REG_N && user->type != X_REG_N) goto fail; reg_m = user->reg; break; case XMTRX_M4: if (user->type != XMTRX_M4) goto fail; reg_m = 4; break; default: printf (_("unhandled %d\n"), arg); goto fail; } } if ( !(valid_arch & this_try->arch)) goto fail; valid_arch &= this_try->arch; return this_try; fail: ; } return 0;}intcheck (operand, low, high) expressionS *operand; int low; int high;{ if (operand->X_op != O_constant || operand->X_add_number < low || operand->X_add_number > high) { as_bad (_("operand must be absolute in range %d..%d"), low, high); } return operand->X_add_number;}static voidinsert (where, how, pcrel, op) char *where; int how; int pcrel; sh_operand_info *op;{ fix_new_exp (frag_now, where - frag_now->fr_literal, 2, &op->immediate, pcrel, how);}static voidbuild_relax (opcode, op) sh_opcode_info *opcode; sh_operand_info *op;{ int high_byte = target_big_endian ? 0 : 1; char *p; if (opcode->arg[0] == A_BDISP8) { int what = (opcode->nibbles[1] & 4) ? COND_JUMP_DELAY : COND_JUMP; p = frag_var (rs_machine_dependent, md_relax_table[C (what, COND32)].rlx_length, md_relax_table[C (what, COND8)].rlx_length, C (what, 0), op->immediate.X_add_symbol, op->immediate.X_add_number, 0); p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]); } else if (opcode->arg[0] == A_BDISP12) { p = frag_var (rs_machine_dependent, md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length, md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length, C (UNCOND_JUMP, 0), op->immediate.X_add_symbol, op->immediate.X_add_number, 0); p[high_byte] = (opcode->nibbles[0] << 4); }}/* Insert ldrs & ldre with fancy relocations that relaxation can recognize. */static char *insert_loop_bounds (output, operand) char *output; sh_operand_info *operand;{ char *name; symbolS *end_sym; /* Since the low byte of the opcode will be overwritten by the reloc, we can just stash the high byte into both bytes and ignore endianness. */ output[0] = 0x8c; output[1] = 0x8c; insert (output, BFD_RELOC_SH_LOOP_START, 1, operand); insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1); if (sh_relax) { static int count = 0; /* If the last loop insn is a two-byte-insn, it is in danger of being swapped with the insn after it. To prevent this, create a new symbol - complete with SH_LABEL reloc - after the last loop insn. If the last loop insn is four bytes long, the symbol will be right in the middle, but four byte insns are not swapped anyways. */ /* A REPEAT takes 6 bytes. The SH has a 32 bit address space. Hence a 9 digit number should be enough to count all REPEATs. */ name = alloca (11); sprintf (name, "_R%x", count++ & 0x3fffffff); end_sym = symbol_new (name, undefined_section, 0, &zero_address_frag); /* Make this a local symbol. */#ifdef OBJ_COFF SF_SET_LOCAL (end_sym);#endif /* OBJ_COFF */ symbol_table_insert (end_sym); end_sym->sy_value = operand[1].immediate; end_sym->sy_value.X_add_number += 2; fix_new (frag_now, frag_now_fix (), 2, end_sym, 0, 1, BFD_RELOC_SH_LABEL); } output = frag_more (2); output[0] = 0x8e; output[1] = 0x8e; insert (output, BFD_RELOC_SH_LOOP_START, 1, operand); insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1); return frag_more (2);}/* Now we know what sort of opcodes it is, let's build the bytes. */static unsigned intbuild_Mytes (opcode, operand) sh_opcode_info *opcode; sh_operand_info *operand;{ int index; char nbuf[4]; char *output = frag_more (2); unsigned int size = 2; int low_byte = target_big_endian ? 1 : 0; nbuf[0] = 0; nbuf[1] = 0; nbuf[2] = 0; nbuf[3] = 0; for (index = 0; index < 4; index++) { sh_nibble_type i = opcode->nibbles[index]; if (i < 16) { nbuf[index] = i; } else { switch (i) { case REG_N: nbuf[index] = reg_n; break; case REG_M: nbuf[index] = reg_m; break; case SDT_REG_N: if (reg_n < 2 || reg_n > 5) as_bad (_("Invalid register: 'r%d'"), reg_n); nbuf[index] = (reg_n & 3) | 4; break; case REG_NM: nbuf[index] = reg_n | (reg_m >> 2); break; case REG_B: nbuf[index] = reg_b | 0x08; break; case IMM0_4BY4: insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand); break; case IMM0_4BY2: insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand); break; case IMM0_4: insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand); break; case IMM1_4BY4: insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand + 1); break; case IMM1_4BY2: insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand + 1); break; case IMM1_4: insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand + 1); break; case IMM0_8BY4: insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand); break; case IMM0_8BY2: insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand); break; case IMM0_8: insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand); break; case IMM1_8BY4: insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand + 1); break; case IMM1_8BY2: insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand + 1); break; case IMM1_8: insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand + 1); break; case PCRELIMM_8BY4: insert (output, BFD_RELOC_SH_PCRELIMM8BY4, 1, operand); break; case PCRELIMM_8BY2: insert (output, BFD_RELOC_SH_PCRELIMM8BY2, 1, operand); break; case REPEAT: output = insert_loop_bounds (output, operand); nbuf[index] = opcode->nibbles[3]; operand += 2; break; default: printf (_("failed for %d\n"), i); } } } if (!target_big_endian) { output[1] = (nbuf[0] << 4) | (nbuf[1]); output[0] = (nbuf[2] << 4) | (nbuf[3]); } else { output[0] = (nbuf[0] << 4) | (nbuf[1]); output[1] = (nbuf[2] << 4) | (nbuf[3]); } return size;}/* Find an opcode at the start of *STR_P in the hash table, and set *STR_P to the first character after the last one read. */static sh_opcode_info *find_cooked_opcode (str_p) char **str_p;{ char *str = *str_p; unsigned char *op_start; unsigned char *op_end; char name[20]; int nlen = 0; /* Drop leading whitespace. */ while (*str == ' ') str++; /* Find the op code end. The pre-processor will eliminate whitespace in front of any '@' after the first argument; we may be called from assemble_ppi, so the opcode might be terminated by an '@'. */ for (op_start = op_end = (unsigned char *) (str); *op_end && nlen < 20 && !is_end_of_line[*op_end] && *op_end != ' ' && *op_end != '@'; op_end++) { unsigned char c = op_start[nlen]; /* The machine independent code will convert CMP/EQ into cmp/EQ because it thinks the '/' is the end of the symbol. Moreover, all but the first sub-insn is a parallel processing insn won't be capitailzed. Instead of hacking up the machine independent code, we just deal with it here. */ c = isupper (c) ? tolower (c) : c; name[nlen] = c; nlen++; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -